// license:BSD-3-Clause // copyright-holders:Miodrag Milanovic,Ryan Holtz,Dario Manesku,Branimir Karadzic,Aaron Giles //============================================================ // // drawbgfx.cpp - BGFX renderer // //============================================================ #define __STDC_LIMIT_MACROS #define __STDC_FORMAT_MACROS #define __STDC_CONSTANT_MACROS #if defined(SDLMAME_WIN32) || defined(OSD_WINDOWS) // standard windows headers #define WIN32_LEAN_AND_MEAN #include #if defined(SDLMAME_WIN32) #include #endif #else #include "sdlinc.h" #endif // MAMEOS headers #include "emu.h" #include "window.h" #include "rendutil.h" #include #include #include #include #include #include "drawbgfx.h" #include "copyutil.h" #include "bgfx/texturemanager.h" #include "bgfx/targetmanager.h" #include "bgfx/shadermanager.h" #include "bgfx/effectmanager.h" #include "bgfx/chainmanager.h" #include "bgfx/effect.h" #include "bgfx/texture.h" #include "bgfx/target.h" #include "bgfx/chain.h" #include "bgfx/vertex.h" #include "bgfx/uniform.h" #include "bgfx/slider.h" //============================================================ // DEBUGGING //============================================================ //============================================================ // CONSTANTS //============================================================ const uint16_t renderer_bgfx::CACHE_SIZE = 1024; const uint32_t renderer_bgfx::PACKABLE_SIZE = 128; const uint32_t renderer_bgfx::WHITE_HASH = 0x87654321; const char* renderer_bgfx::WINDOW_PREFIX = "Window 0, "; //============================================================ // MACROS //============================================================ #define GIBBERISH (0) //============================================================ // TYPES //============================================================ bool renderer_bgfx::s_window_set = false; //============================================================ // renderer_bgfx::create //============================================================ #ifdef OSD_SDL static void* sdlNativeWindowHandle(SDL_Window* _window) { SDL_SysWMinfo wmi; SDL_VERSION(&wmi.version); if (!SDL_GetWindowWMInfo(_window, &wmi)) { return nullptr; } # if BX_PLATFORM_LINUX || BX_PLATFORM_BSD return (void*)wmi.info.x11.window; # elif BX_PLATFORM_OSX return wmi.info.cocoa.window; # elif BX_PLATFORM_WINDOWS return wmi.info.win.window; # elif BX_PLATFORM_STEAMLINK return wmi.info.vivante.window; # elif BX_PLATFORM_EMSCRIPTEN return nullptr; # endif // BX_PLATFORM_ } #endif int renderer_bgfx::create() { // create renderer osd_options& options = downcast(window().machine().options()); osd_dim wdim = window().get_size(); m_width[window().m_index] = wdim.width(); m_height[window().m_index] = wdim.height(); if (window().m_index == 0) { if (!s_window_set) { s_window_set = true; ScreenVertex::init(); } else { bgfx::shutdown(); bgfx::PlatformData blank_pd; memset(&blank_pd, 0, sizeof(bgfx::PlatformData)); bgfx::setPlatformData(blank_pd); } #ifdef OSD_WINDOWS bgfx::winSetHwnd(window().m_hwnd); #else bgfx::sdlSetWindow(window().sdl_window()); #endif std::string backend(options.bgfx_backend()); if (backend == "auto") { bgfx::init(); } else if (backend == "dx9" || backend == "d3d9") { bgfx::init(bgfx::RendererType::Direct3D9); } else if (backend == "dx11" || backend == "d3d11") { bgfx::init(bgfx::RendererType::Direct3D11); } else if (backend == "gles") { bgfx::init(bgfx::RendererType::OpenGLES); } else if (backend == "glsl" || backend == "opengl") { bgfx::init(bgfx::RendererType::OpenGL); } else if (backend == "metal") { bgfx::init(bgfx::RendererType::Metal); } else { printf("Unknown backend type '%s', going with auto-detection\n", backend.c_str()); bgfx::init(); } bgfx::reset(m_width[window().m_index], m_height[window().m_index], video_config.waitvsync ? BGFX_RESET_VSYNC : BGFX_RESET_NONE); // Enable debug text. bgfx::setDebug(options.bgfx_debug() ? BGFX_DEBUG_STATS : BGFX_DEBUG_TEXT); m_dimensions = osd_dim(m_width[0], m_height[0]); } m_textures = new texture_manager(); m_targets = new target_manager(options, *m_textures); m_shaders = new shader_manager(options); m_effects = new effect_manager(options, *m_shaders); if (window().m_index != 0) { #ifdef OSD_WINDOWS m_framebuffer = m_targets->create_backbuffer(window().m_hwnd, m_width[window().m_index], m_height[window().m_index]); #else m_framebuffer = m_targets->create_backbuffer(sdlNativeWindowHandle(window().sdl_window()), m_width[window().m_index], m_height[window().m_index]); #endif bgfx::touch(window().m_index); } // Create program from shaders. m_gui_effect[0] = m_effects->effect("gui_opaque"); m_gui_effect[1] = m_effects->effect("gui_blend"); m_gui_effect[2] = m_effects->effect("gui_multiply"); m_gui_effect[3] = m_effects->effect("gui_add"); m_screen_effect[0] = m_effects->effect("screen_opaque"); m_screen_effect[1] = m_effects->effect("screen_blend"); m_screen_effect[2] = m_effects->effect("screen_multiply"); m_screen_effect[3] = m_effects->effect("screen_add"); m_chains = new chain_manager(options, *m_textures, *m_targets, *m_effects, m_width[window().m_index], m_height[window().m_index]); m_screen_chain = m_chains->chain(options.bgfx_screen_chain(), window().machine(), window().m_index); m_sliders_dirty = true; uint32_t flags = BGFX_TEXTURE_U_CLAMP | BGFX_TEXTURE_V_CLAMP | BGFX_TEXTURE_MIN_POINT | BGFX_TEXTURE_MAG_POINT | BGFX_TEXTURE_MIP_POINT; m_texture_cache = m_textures->create_texture("#cache", bgfx::TextureFormat::RGBA8, CACHE_SIZE, CACHE_SIZE, nullptr, flags); uint32_t shadow_flags = 0;//BGFX_TEXTURE_MIN_POINT | BGFX_TEXTURE_MAG_POINT | BGFX_TEXTURE_MIP_POINT; m_textures->create_png_texture(window().machine().options().art_path(), options.bgfx_shadow_mask(), "shadow", shadow_flags); memset(m_white, 0xff, sizeof(uint32_t) * 16 * 16); m_texinfo.push_back(rectangle_packer::packable_rectangle(WHITE_HASH, PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32), 16, 16, 16, nullptr, m_white)); return 0; } //============================================================ // destructor //============================================================ renderer_bgfx::~renderer_bgfx() { // Cleanup. delete m_chains; delete m_effects; delete m_shaders; delete m_textures; delete m_targets; } void renderer_bgfx::exit() { bgfx::shutdown(); s_window_set = false; } //============================================================ // drawsdl_xy_to_render_target //============================================================ #ifdef OSD_SDL int renderer_bgfx::xy_to_render_target(int x, int y, int *xt, int *yt) { *xt = x; *yt = y; if (*xt<0 || *xt >= m_dimensions.width()) return 0; if (*yt<0 || *yt >= m_dimensions.height()) return 0; return 1; } #endif //============================================================ // drawbgfx_window_draw //============================================================ bgfx::VertexDecl ScreenVertex::ms_decl; void renderer_bgfx::put_packed_quad(render_primitive *prim, UINT32 hash, ScreenVertex* vertex) { rectangle_packer::packed_rectangle& rect = m_hash_to_entry[hash]; float u0 = float(rect.x()) / float(CACHE_SIZE); float v0 = float(rect.y()) / float(CACHE_SIZE); float u1 = u0 + float(rect.width()) / float(CACHE_SIZE); float v1 = v0 + float(rect.height()) / float(CACHE_SIZE); u1 -= 0.5f / float(CACHE_SIZE); v1 -= 0.5f / float(CACHE_SIZE); u0 += 0.5f / float(CACHE_SIZE); v0 += 0.5f / float(CACHE_SIZE); UINT32 rgba = u32Color(prim->color.r * 255, prim->color.g * 255, prim->color.b * 255, prim->color.a * 255); float x[4] = { prim->bounds.x0, prim->bounds.x1, prim->bounds.x0, prim->bounds.x1 }; float y[4] = { prim->bounds.y0, prim->bounds.y0, prim->bounds.y1, prim->bounds.y1 }; float u[4] = { u0, u1, u0, u1 }; float v[4] = { v0, v0, v1, v1 }; if (PRIMFLAG_GET_TEXORIENT(prim->flags) & ORIENTATION_SWAP_XY) { std::swap(u[1], u[2]); std::swap(v[1], v[2]); } if (PRIMFLAG_GET_TEXORIENT(prim->flags) & ORIENTATION_FLIP_X) { std::swap(u[0], u[1]); std::swap(v[0], v[1]); std::swap(u[2], u[3]); std::swap(v[2], v[3]); } if (PRIMFLAG_GET_TEXORIENT(prim->flags) & ORIENTATION_FLIP_Y) { std::swap(u[0], u[2]); std::swap(v[0], v[2]); std::swap(u[1], u[3]); std::swap(v[1], v[3]); } vertex[0].m_x = x[0]; // 0 vertex[0].m_y = y[0]; vertex[0].m_z = 0; vertex[0].m_rgba = rgba; vertex[0].m_u = u[0]; vertex[0].m_v = v[0]; vertex[1].m_x = x[1]; // 1 vertex[1].m_y = y[1]; vertex[1].m_z = 0; vertex[1].m_rgba = rgba; vertex[1].m_u = u[1]; vertex[1].m_v = v[1]; vertex[2].m_x = x[3]; // 3 vertex[2].m_y = y[3]; vertex[2].m_z = 0; vertex[2].m_rgba = rgba; vertex[2].m_u = u[3]; vertex[2].m_v = v[3]; vertex[3].m_x = x[3]; // 3 vertex[3].m_y = y[3]; vertex[3].m_z = 0; vertex[3].m_rgba = rgba; vertex[3].m_u = u[3]; vertex[3].m_v = v[3]; vertex[4].m_x = x[2]; // 2 vertex[4].m_y = y[2]; vertex[4].m_z = 0; vertex[4].m_rgba = rgba; vertex[4].m_u = u[2]; vertex[4].m_v = v[2]; vertex[5].m_x = x[0]; // 0 vertex[5].m_y = y[0]; vertex[5].m_z = 0; vertex[5].m_rgba = rgba; vertex[5].m_u = u[0]; vertex[5].m_v = v[0]; } void renderer_bgfx::process_screen_quad(int view, render_primitive* prim) { uint32_t texture_flags = BGFX_TEXTURE_U_CLAMP | BGFX_TEXTURE_V_CLAMP; if (video_config.filter == 0) { texture_flags |= BGFX_TEXTURE_MIN_POINT | BGFX_TEXTURE_MAG_POINT | BGFX_TEXTURE_MIP_POINT; } uint16_t tex_width(prim->texture.width); uint16_t tex_height(prim->texture.height); const bgfx::Memory* mem = mame_texture_data_to_bgfx_texture_data(prim->flags & PRIMFLAG_TEXFORMAT_MASK, tex_width, tex_height, prim->texture.rowpixels, prim->texture.palette, prim->texture.base); bgfx_texture *texture = new bgfx_texture("screen", bgfx::TextureFormat::RGBA8, tex_width, tex_height, mem); m_textures->add_provider("screen", texture); int screens = 0; screen_device_iterator iter(window().machine().root_device()); for (const screen_device *screen = iter.first(); screen != nullptr; screen = iter.next()) { screens++; } m_targets->update_guest_targets(tex_width, tex_height); m_targets->update_window_count(screens); m_screen_chain->process(prim, view, view / m_screen_chain->applicable_passes(), *m_textures, window(), get_blend_state(PRIMFLAG_GET_BLENDMODE(prim->flags))); m_textures->add_provider("screen", nullptr); delete texture; } void renderer_bgfx::render_post_screen_quad(int view, render_primitive* prim, bgfx::TransientVertexBuffer* buffer, int32_t screen) { ScreenVertex* vertex = reinterpret_cast(buffer->data); float x[4] = { prim->bounds.x0, prim->bounds.x1, prim->bounds.x0, prim->bounds.x1 }; float y[4] = { prim->bounds.y0, prim->bounds.y0, prim->bounds.y1, prim->bounds.y1 }; float u[4] = { prim->texcoords.tl.u, prim->texcoords.tr.u, prim->texcoords.bl.u, prim->texcoords.br.u }; float v[4] = { prim->texcoords.tl.v, prim->texcoords.tr.v, prim->texcoords.bl.v, prim->texcoords.br.v }; if (false)//PRIMFLAG_GET_TEXORIENT(prim->flags) & ORIENTATION_SWAP_XY) { std::swap(u[1], u[2]); std::swap(v[1], v[2]); } if (false)//PRIMFLAG_GET_TEXORIENT(prim->flags) & ORIENTATION_FLIP_X) { std::swap(u[0], u[1]); std::swap(v[0], v[1]); std::swap(u[2], u[3]); std::swap(v[2], v[3]); } if (false)//PRIMFLAG_GET_TEXORIENT(prim->flags) & ORIENTATION_FLIP_Y) { std::swap(u[0], u[2]); std::swap(v[0], v[2]); std::swap(u[1], u[3]); std::swap(v[1], v[3]); } vertex[0].m_x = x[0]; vertex[0].m_y = y[0]; vertex[0].m_z = 0; vertex[0].m_rgba = 0xffffffff; vertex[0].m_u = u[0]; vertex[0].m_v = v[0]; vertex[1].m_x = x[1]; vertex[1].m_y = y[1]; vertex[1].m_z = 0; vertex[1].m_rgba = 0xffffffff; vertex[1].m_u = u[1]; vertex[1].m_v = v[1]; vertex[2].m_x = x[3]; vertex[2].m_y = y[3]; vertex[2].m_z = 0; vertex[2].m_rgba = 0xffffffff; vertex[2].m_u = u[3]; vertex[2].m_v = v[3]; vertex[3].m_x = x[3]; vertex[3].m_y = y[3]; vertex[3].m_z = 0; vertex[3].m_rgba = 0xffffffff; vertex[3].m_u = u[3]; vertex[3].m_v = v[3]; vertex[4].m_x = x[2]; vertex[4].m_y = y[2]; vertex[4].m_z = 0; vertex[4].m_rgba = 0xffffffff; vertex[4].m_u = u[2]; vertex[4].m_v = v[2]; vertex[5].m_x = x[0]; vertex[5].m_y = y[0]; vertex[5].m_z = 0; vertex[5].m_rgba = 0xffffffff; vertex[5].m_u = u[0]; vertex[5].m_v = v[0]; UINT32 blend = PRIMFLAG_GET_BLENDMODE(prim->flags); bgfx::setVertexBuffer(buffer); bgfx::setTexture(0, m_screen_effect[blend]->uniform("s_tex")->handle(), m_targets->target("output" + std::to_string(screen))->texture()); m_screen_effect[blend]->submit(view); } void renderer_bgfx::render_textured_quad(int view, render_primitive* prim, bgfx::TransientVertexBuffer* buffer) { ScreenVertex* vertex = reinterpret_cast(buffer->data); UINT32 rgba = u32Color(prim->color.r * 255, prim->color.g * 255, prim->color.b * 255, prim->color.a * 255); vertex[0].m_x = prim->bounds.x0; vertex[0].m_y = prim->bounds.y0; vertex[0].m_z = 0; vertex[0].m_rgba = rgba; vertex[0].m_u = prim->texcoords.tl.u; vertex[0].m_v = prim->texcoords.tl.v; vertex[1].m_x = prim->bounds.x1; vertex[1].m_y = prim->bounds.y0; vertex[1].m_z = 0; vertex[1].m_rgba = rgba; vertex[1].m_u = prim->texcoords.tr.u; vertex[1].m_v = prim->texcoords.tr.v; vertex[2].m_x = prim->bounds.x1; vertex[2].m_y = prim->bounds.y1; vertex[2].m_z = 0; vertex[2].m_rgba = rgba; vertex[2].m_u = prim->texcoords.br.u; vertex[2].m_v = prim->texcoords.br.v; vertex[3].m_x = prim->bounds.x1; vertex[3].m_y = prim->bounds.y1; vertex[3].m_z = 0; vertex[3].m_rgba = rgba; vertex[3].m_u = prim->texcoords.br.u; vertex[3].m_v = prim->texcoords.br.v; vertex[4].m_x = prim->bounds.x0; vertex[4].m_y = prim->bounds.y1; vertex[4].m_z = 0; vertex[4].m_rgba = rgba; vertex[4].m_u = prim->texcoords.bl.u; vertex[4].m_v = prim->texcoords.bl.v; vertex[5].m_x = prim->bounds.x0; vertex[5].m_y = prim->bounds.y0; vertex[5].m_z = 0; vertex[5].m_rgba = rgba; vertex[5].m_u = prim->texcoords.tl.u; vertex[5].m_v = prim->texcoords.tl.v; uint32_t texture_flags = BGFX_TEXTURE_U_CLAMP | BGFX_TEXTURE_V_CLAMP; if (video_config.filter == 0) { texture_flags |= BGFX_TEXTURE_MIN_POINT | BGFX_TEXTURE_MAG_POINT | BGFX_TEXTURE_MIP_POINT; } uint16_t tex_width(prim->texture.width); uint16_t tex_height(prim->texture.height); const bgfx::Memory* mem = mame_texture_data_to_bgfx_texture_data(prim->flags & PRIMFLAG_TEXFORMAT_MASK, tex_width, tex_height, prim->texture.rowpixels, prim->texture.palette, prim->texture.base); bgfx::TextureHandle texture = bgfx::createTexture2D(tex_width, tex_height, 1, bgfx::TextureFormat::RGBA8, texture_flags, mem); bgfx_effect** effects = PRIMFLAG_GET_SCREENTEX(prim->flags) ? m_screen_effect : m_gui_effect; UINT32 blend = PRIMFLAG_GET_BLENDMODE(prim->flags); bgfx::setVertexBuffer(buffer); bgfx::setTexture(0, effects[blend]->uniform("s_tex")->handle(), texture); effects[blend]->submit(view); bgfx::destroyTexture(texture); } #define MAX_TEMP_COORDS 100 void renderer_bgfx::put_polygon(const float* coords, UINT32 num_coords, float r, UINT32 rgba, ScreenVertex* vertex) { float tempCoords[MAX_TEMP_COORDS * 3]; float tempNormals[MAX_TEMP_COORDS * 2]; rectangle_packer::packed_rectangle& rect = m_hash_to_entry[WHITE_HASH]; float u0 = float(rect.x()) / float(CACHE_SIZE); float v0 = float(rect.y()) / float(CACHE_SIZE); num_coords = num_coords < MAX_TEMP_COORDS ? num_coords : MAX_TEMP_COORDS; for (uint32_t ii = 0, jj = num_coords - 1; ii < num_coords; jj = ii++) { const float* v0 = &coords[jj * 3]; const float* v1 = &coords[ii * 3]; float dx = v1[0] - v0[0]; float dy = v1[1] - v0[1]; float d = sqrtf(dx * dx + dy * dy); if (d > 0) { d = 1.0f / d; dx *= d; dy *= d; } tempNormals[jj * 2 + 0] = dy; tempNormals[jj * 2 + 1] = -dx; } for (uint32_t ii = 0, jj = num_coords - 1; ii < num_coords; jj = ii++) { float dlx0 = tempNormals[jj * 2 + 0]; float dly0 = tempNormals[jj * 2 + 1]; float dlx1 = tempNormals[ii * 2 + 0]; float dly1 = tempNormals[ii * 2 + 1]; float dmx = (dlx0 + dlx1) * 0.5f; float dmy = (dly0 + dly1) * 0.5f; float dmr2 = dmx * dmx + dmy * dmy; if (dmr2 > 0.000001f) { float scale = 1.0f / dmr2; if (scale > 10.0f) { scale = 10.0f; } dmx *= scale; dmy *= scale; } tempCoords[ii * 3 + 0] = coords[ii * 3 + 0] + dmx * r; tempCoords[ii * 3 + 1] = coords[ii * 3 + 1] + dmy * r; tempCoords[ii * 3 + 2] = coords[ii * 3 + 2]; } int vertIndex = 0; UINT32 trans = rgba & 0x00ffffff; for (uint32_t ii = 0, jj = num_coords - 1; ii < num_coords; jj = ii++) { vertex[vertIndex].m_x = coords[ii * 3 + 0]; vertex[vertIndex].m_y = coords[ii * 3 + 1]; vertex[vertIndex].m_z = coords[ii * 3 + 2]; vertex[vertIndex].m_rgba = rgba; vertex[vertIndex].m_u = u0; vertex[vertIndex].m_v = v0; vertIndex++; vertex[vertIndex].m_x = coords[jj * 3 + 0]; vertex[vertIndex].m_y = coords[jj * 3 + 1]; vertex[vertIndex].m_z = coords[jj * 3 + 2]; vertex[vertIndex].m_rgba = rgba; vertex[vertIndex].m_u = u0; vertex[vertIndex].m_v = v0; vertIndex++; vertex[vertIndex].m_x = tempCoords[jj * 3 + 0]; vertex[vertIndex].m_y = tempCoords[jj * 3 + 1]; vertex[vertIndex].m_z = tempCoords[jj * 3 + 2]; vertex[vertIndex].m_rgba = trans; vertex[vertIndex].m_u = u0; vertex[vertIndex].m_v = v0; vertIndex++; vertex[vertIndex].m_x = tempCoords[jj * 3 + 0]; vertex[vertIndex].m_y = tempCoords[jj * 3 + 1]; vertex[vertIndex].m_z = tempCoords[jj * 3 + 2]; vertex[vertIndex].m_rgba = trans; vertex[vertIndex].m_u = u0; vertex[vertIndex].m_v = v0; vertIndex++; vertex[vertIndex].m_x = tempCoords[ii * 3 + 0]; vertex[vertIndex].m_y = tempCoords[ii * 3 + 1]; vertex[vertIndex].m_z = tempCoords[ii * 3 + 2]; vertex[vertIndex].m_rgba = trans; vertex[vertIndex].m_u = u0; vertex[vertIndex].m_v = v0; vertIndex++; vertex[vertIndex].m_x = coords[ii * 3 + 0]; vertex[vertIndex].m_y = coords[ii * 3 + 1]; vertex[vertIndex].m_z = coords[ii * 3 + 2]; vertex[vertIndex].m_rgba = rgba; vertex[vertIndex].m_u = u0; vertex[vertIndex].m_v = v0; vertIndex++; } for (uint32_t ii = 2; ii < num_coords; ++ii) { vertex[vertIndex].m_x = coords[0]; vertex[vertIndex].m_y = coords[1]; vertex[vertIndex].m_z = coords[2]; vertex[vertIndex].m_rgba = rgba; vertex[vertIndex].m_u = u0; vertex[vertIndex].m_v = v0; vertIndex++; vertex[vertIndex].m_x = coords[(ii - 1) * 3 + 0]; vertex[vertIndex].m_y = coords[(ii - 1) * 3 + 1]; vertex[vertIndex].m_z = coords[(ii - 1) * 3 + 2]; vertex[vertIndex].m_rgba = rgba; vertex[vertIndex].m_u = u0; vertex[vertIndex].m_v = v0; vertIndex++; vertex[vertIndex].m_x = coords[ii * 3 + 0]; vertex[vertIndex].m_y = coords[ii * 3 + 1]; vertex[vertIndex].m_z = coords[ii * 3 + 2]; vertex[vertIndex].m_rgba = rgba; vertex[vertIndex].m_u = u0; vertex[vertIndex].m_v = v0; vertIndex++; } } void renderer_bgfx::put_line(float x0, float y0, float x1, float y1, float r, UINT32 rgba, ScreenVertex* vertex, float fth) { float dx = x1 - x0; float dy = y1 - y0; float d = sqrtf(dx * dx + dy * dy); if (d > 0.0001f) { d = 1.0f / d; dx *= d; dy *= d; } float nx = dy; float ny = -dx; float verts[4 * 3]; r -= fth; r *= 0.5f; if (r < 0.01f) { r = 0.01f; } dx *= r; dy *= r; nx *= r; ny *= r; verts[0] = x0 - dx - nx; verts[1] = y0 - dy - ny; verts[2] = 0; verts[3] = x0 - dx + nx; verts[4] = y0 - dy + ny; verts[5] = 0; verts[6] = x1 + dx + nx; verts[7] = y1 + dy + ny; verts[8] = 0; verts[9] = x1 + dx - nx; verts[10] = y1 + dy - ny; verts[11] = 0; put_polygon(verts, 4, fth, rgba, vertex); } uint32_t renderer_bgfx::u32Color(uint32_t r, uint32_t g, uint32_t b, uint32_t a = 255) { return (a << 24) | (b << 16) | (g << 8) | r; } const bgfx::Memory* renderer_bgfx::mame_texture_data_to_bgfx_texture_data(UINT32 format, int width, int height, int rowpixels, const rgb_t *palette, void *base) { const bgfx::Memory* mem = bgfx::alloc(width * height * 4); for (int y = 0; y < height; y++) { switch (format) { case PRIMFLAG_TEXFORMAT(TEXFORMAT_PALETTE16): copy_util::copyline_palette16((UINT32*)mem->data + y * width, (UINT16*)base + y * rowpixels, width, palette); break; case PRIMFLAG_TEXFORMAT(TEXFORMAT_PALETTEA16): copy_util::copyline_palettea16((UINT32*)mem->data + y * width, (UINT16*)base + y * rowpixels, width, palette); break; case PRIMFLAG_TEXFORMAT(TEXFORMAT_YUY16): copy_util::copyline_yuy16_to_argb((UINT32*)mem->data + y * width, (UINT16*)base + y * rowpixels, width, palette, 1); break; case PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32): copy_util::copyline_argb32((UINT32*)mem->data + y * width, (UINT32*)base + y * rowpixels, width, palette); break; case PRIMFLAG_TEXFORMAT(TEXFORMAT_RGB32): copy_util::copyline_rgb32((UINT32*)mem->data + y * width, (UINT32*)base + y * rowpixels, width, palette); break; default: break; } } return mem; } int renderer_bgfx::handle_screen_chains() { if (!m_screen_chain) { return 0; } window().m_primlist->acquire_lock(); // process render_primitive *prim = window().m_primlist->first(); int seen = 0; while (prim != nullptr) { if (PRIMFLAG_GET_SCREENTEX(prim->flags)) { process_screen_quad(m_screen_chain->applicable_passes() * seen, prim); seen++; } prim = prim->next(); } window().m_primlist->release_lock(); uint32_t total_passes = seen * m_screen_chain->applicable_passes(); bgfx::setViewFrameBuffer(total_passes, BGFX_INVALID_HANDLE); return total_passes; } int renderer_bgfx::draw(int update) { int window_index = window().m_index; int post_view_index = handle_screen_chains(); int view_index = window_index; int first_view_index = 0; if (m_screen_chain && post_view_index > 0) { view_index += post_view_index; first_view_index = post_view_index; } // Set view 0 default viewport. osd_dim wdim = window().get_size(); m_width[window_index] = wdim.width(); m_height[window_index] = wdim.height(); if (window_index == 0) { if ((m_dimensions != osd_dim(m_width[window_index], m_height[window_index]))) { bgfx::reset(m_width[window_index], m_height[window_index], video_config.waitvsync ? BGFX_RESET_VSYNC : BGFX_RESET_NONE); m_dimensions = osd_dim(m_width[window_index], m_height[window_index]); } } else { if ((m_dimensions != osd_dim(m_width[window_index], m_height[window_index]))) { bgfx::reset(window().m_main->get_size().width(), window().m_main->get_size().height(), video_config.waitvsync ? BGFX_RESET_VSYNC : BGFX_RESET_NONE); delete m_framebuffer; #ifdef OSD_WINDOWS m_framebuffer = m_targets->create_backbuffer(window().m_hwnd, m_width[window_index], m_height[window_index]); #else m_framebuffer = m_targets->create_backbuffer(sdlNativeWindowHandle(window().sdl_window()), m_width[window_index], m_height[window_index]); #endif bgfx::setViewFrameBuffer(view_index, m_framebuffer->target()); m_dimensions = osd_dim(m_width[window_index], m_height[window_index]); bgfx::setViewClear(view_index , BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH , 0x000000ff , 1.0f , 0 ); bgfx::touch(view_index); bgfx::frame(); return 0; } } if (view_index != first_view_index) { bgfx::setViewFrameBuffer(view_index, m_framebuffer->target()); } bgfx::setViewSeq(view_index, true); bgfx::setViewRect(view_index, 0, 0, m_width[window_index], m_height[window_index]); // Setup view transform. float proj[16]; bx::mtxOrtho(proj, 0.0f, m_width[window_index], m_height[window_index], 0.0f, 0.0f, 100.0f); bgfx::setViewTransform(view_index, nullptr, proj); bgfx::setViewClear(view_index , BGFX_CLEAR_COLOR | BGFX_CLEAR_DEPTH , 0x000000ff , 1.0f , 0 ); window().m_primlist->acquire_lock(); // Mark our texture atlas as dirty if we need to do so bool atlas_valid = update_atlas(); render_primitive *prim = window().m_primlist->first(); int32_t screen = 0; while (prim != nullptr) { UINT32 blend = PRIMFLAG_GET_BLENDMODE(prim->flags); bgfx::TransientVertexBuffer buffer; allocate_buffer(prim, blend, &buffer); buffer_status status = buffer_primitives(view_index, atlas_valid, &prim, &buffer, screen); if (status == BUFFER_SCREEN) { screen++; } if (status != BUFFER_EMPTY && status != BUFFER_SCREEN) { bgfx::setVertexBuffer(&buffer); bgfx::setTexture(0, m_gui_effect[blend]->uniform("s_tex")->handle(), m_texture_cache->texture()); m_gui_effect[blend]->submit(view_index); } if (status != BUFFER_DONE && status != BUFFER_PRE_FLUSH) { prim = prim->next(); } } window().m_primlist->release_lock(); // This dummy draw call is here to make sure that view 0 is cleared // if no other draw calls are submitted to view 0. bgfx::touch(view_index); // Advance to next frame. Rendering thread will be kicked to // process submitted rendering primitives. if (view_index == first_view_index) { bgfx::frame(); } return 0; } renderer_bgfx::buffer_status renderer_bgfx::buffer_primitives(int view, bool atlas_valid, render_primitive** prim, bgfx::TransientVertexBuffer* buffer, int32_t screen) { int vertices = 0; UINT32 blend = PRIMFLAG_GET_BLENDMODE((*prim)->flags); while (*prim != nullptr) { switch ((*prim)->type) { case render_primitive::LINE: put_line((*prim)->bounds.x0, (*prim)->bounds.y0, (*prim)->bounds.x1, (*prim)->bounds.y1, 1.0f, u32Color((*prim)->color.r * 255, (*prim)->color.g * 255, (*prim)->color.b * 255, (*prim)->color.a * 255), (ScreenVertex*)buffer->data + vertices, 1.0f); vertices += 30; break; case render_primitive::QUAD: if ((*prim)->texture.base == nullptr) { put_packed_quad(*prim, WHITE_HASH, (ScreenVertex*)buffer->data + vertices); vertices += 6; } else { const UINT32 hash = get_texture_hash(*prim); if (atlas_valid && (*prim)->packable(PACKABLE_SIZE) && hash != 0 && m_hash_to_entry[hash].hash()) { put_packed_quad(*prim, hash, (ScreenVertex*)buffer->data + vertices); vertices += 6; } else { if (vertices > 0) { return BUFFER_PRE_FLUSH; } if (PRIMFLAG_GET_SCREENTEX((*prim)->flags) && m_screen_chain != nullptr) { render_post_screen_quad(view, *prim, buffer, screen); return BUFFER_SCREEN; } else { render_textured_quad(view, *prim, buffer); return BUFFER_EMPTY; } } } break; default: // Unhandled break; } if ((*prim)->next() != nullptr && PRIMFLAG_GET_BLENDMODE((*prim)->next()->flags) != blend) { break; } *prim = (*prim)->next(); } if (*prim == nullptr) { return BUFFER_DONE; } if (vertices == 0) { return BUFFER_EMPTY; } return BUFFER_FLUSH; } uint64_t renderer_bgfx::get_blend_state(UINT32 blend) { switch (blend) { case BLENDMODE_ALPHA: return BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_INV_SRC_ALPHA); case BLENDMODE_RGB_MULTIPLY: return BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_DST_COLOR, BGFX_STATE_BLEND_ZERO); case BLENDMODE_ADD: return BGFX_STATE_BLEND_FUNC(BGFX_STATE_BLEND_SRC_ALPHA, BGFX_STATE_BLEND_ONE); default: return 0L; } return 0L; } void renderer_bgfx::set_bgfx_state(UINT32 blend) { uint64_t flags = BGFX_STATE_RGB_WRITE | BGFX_STATE_ALPHA_WRITE | BGFX_STATE_DEPTH_TEST_ALWAYS; bgfx::setState(flags | get_blend_state(blend)); } bool renderer_bgfx::update_atlas() { bool atlas_dirty = check_for_dirty_atlas(); if (atlas_dirty) { m_hash_to_entry.clear(); std::vector> packed; if (m_packer.pack(m_texinfo, packed, CACHE_SIZE)) { process_atlas_packs(packed); } else { packed.clear(); m_texinfo.clear(); m_texinfo.push_back(rectangle_packer::packable_rectangle(WHITE_HASH, PRIMFLAG_TEXFORMAT(TEXFORMAT_ARGB32), 16, 16, 16, nullptr, m_white)); m_packer.pack(m_texinfo, packed, CACHE_SIZE); process_atlas_packs(packed); return false; } } return true; } void renderer_bgfx::process_atlas_packs(std::vector>& packed) { for (std::vector pack : packed) { for (rectangle_packer::packed_rectangle rect : pack) { if (rect.hash() == 0xffffffff) { continue; } m_hash_to_entry[rect.hash()] = rect; const bgfx::Memory* mem = mame_texture_data_to_bgfx_texture_data(rect.format(), rect.width(), rect.height(), rect.rowpixels(), rect.palette(), rect.base()); bgfx::updateTexture2D(m_texture_cache->texture(), 0, rect.x(), rect.y(), rect.width(), rect.height(), mem); } } } UINT32 renderer_bgfx::get_texture_hash(render_primitive *prim) { #if GIBBERISH UINT32 xor_value = 0x87; UINT32 hash = 0xdabeefed; int bpp = 2; UINT32 format = PRIMFLAG_GET_TEXFORMAT(prim->flags); if (format == TEXFORMAT_ARGB32 || format == TEXFORMAT_RGB32) { bpp = 4; } for (int y = 0; y < prim->texture.height; y++) { UINT8 *base = reinterpret_cast(prim->texture.base) + prim->texture.rowpixels * y; for (int x = 0; x < prim->texture.width * bpp; x++) { hash += base[x] ^ xor_value; } } return hash; #else return (reinterpret_cast(prim->texture.base)) & 0xffffffff; #endif } bool renderer_bgfx::check_for_dirty_atlas() { bool atlas_dirty = false; std::map acquired_infos; for (render_primitive *prim = window().m_primlist->first(); prim != nullptr; prim = prim->next()) { bool pack = prim->packable(PACKABLE_SIZE); if (prim->type == render_primitive::QUAD && prim->texture.base != nullptr && pack) { const UINT32 hash = get_texture_hash(prim); // If this texture is packable and not currently in the atlas, prepare the texture for putting in the atlas if ((hash != 0 && m_hash_to_entry[hash].hash() == 0 && acquired_infos[hash].hash() == 0) || (hash != 0 && m_hash_to_entry[hash].hash() != hash && acquired_infos[hash].hash() == 0)) { // Create create the texture and mark the atlas dirty atlas_dirty = true; m_texinfo.push_back(rectangle_packer::packable_rectangle(hash, prim->flags & PRIMFLAG_TEXFORMAT_MASK, prim->texture.width, prim->texture.height, prim->texture.rowpixels, prim->texture.palette, prim->texture.base)); acquired_infos[hash] = m_texinfo[m_texinfo.size() - 1]; } } } if (m_texinfo.size() == 1) { atlas_dirty = true; } return atlas_dirty; } void renderer_bgfx::allocate_buffer(render_primitive *prim, UINT32 blend, bgfx::TransientVertexBuffer *buffer) { int vertices = 0; bool mode_switched = false; while (prim != nullptr && !mode_switched) { switch (prim->type) { case render_primitive::LINE: vertices += 30; break; case render_primitive::QUAD: if (!prim->packable(PACKABLE_SIZE)) { if (prim->texture.base == nullptr) { vertices += 6; } else { if (vertices == 0) { vertices += 6; } mode_switched = true; } } else { vertices += 6; } break; default: // Do nothing break; } prim = prim->next(); if (prim != nullptr && PRIMFLAG_GET_BLENDMODE(prim->flags) != blend) { mode_switched = true; } } if (vertices > 0 && bgfx::checkAvailTransientVertexBuffer(vertices, ScreenVertex::ms_decl)) { bgfx::allocTransientVertexBuffer(buffer, vertices, ScreenVertex::ms_decl); } } slider_state* renderer_bgfx::get_slider_list() { if (!m_screen_chain) { return nullptr; } slider_state *listhead = nullptr; slider_state **tailptr = &listhead; std::vector sliders = m_screen_chain->sliders(); for (bgfx_slider* slider : sliders) { if (*tailptr == nullptr) { *tailptr = slider->core_slider(); } else { (*tailptr)->next = slider->core_slider(); tailptr = &(*tailptr)->next; } } if (*tailptr != nullptr) { (*tailptr)->next = nullptr; } m_sliders_dirty = false; return listhead; }