// license:BSD-3-Clause // copyright-holders:Olivier Galibert, R. Belmont //============================================================ // // drawogl.c - SDL software and OpenGL implementation // // SDLMAME by Olivier Galibert and R. Belmont // // Note: D3D9 goes to a lot of trouble to fiddle with MODULATE // mode on textures. That is the default in OpenGL so we // don't have to touch it. // //============================================================ // standard C headers #include #include // MAME headers #include "osdcomm.h" #include "emu.h" #include "emuopts.h" #ifdef OSD_MAC #define GL_SILENCE_DEPRECATION (1) #endif #if !defined(OSD_WINDOWS) && !defined(OSD_MAC) // standard SDL headers #define TOBEMIGRATED 1 #include #endif #include "modules/lib/osdlib.h" #include "modules/lib/osdobj_common.h" // OpenGL headers #include "modules/opengl/osd_opengl.h" #include "modules/opengl/gl_shader_tool.h" #include "modules/opengl/gl_shader_mgr.h" #if defined(SDLMAME_MACOSX) || defined(OSD_MAC) #include #include #include #include #ifndef APIENTRY #define APIENTRY #endif #ifndef APIENTRYP #define APIENTRYP APIENTRY * #endif typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum, GLuint); typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum, GLsizeiptr, const GLvoid *, GLenum); typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum, GLintptr, GLsizeiptr, const GLvoid *); typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERPROC) (GLenum, GLenum); typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum); typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei, const GLuint *); typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFEREXTPROC) (GLuint framebuffer); typedef void (APIENTRYP PFNGLBINDFRAMEBUFFEREXTPROC) (GLenum m_target, GLuint framebuffer); typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSEXTPROC) (GLsizei n, const GLuint *framebuffers); typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSEXTPROC) (GLsizei n, GLuint *framebuffers); typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) (GLenum m_target); typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) (GLenum m_target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); typedef void (APIENTRYP PFNGLGENRENDERBUFFERSEXTPROC) (GLsizei n, GLuint *renderbuffers); typedef void (APIENTRYP PFNGLBINDRENDERBUFFEREXTPROC) (GLenum m_target, GLuint renderbuffer); typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEEXTPROC) (GLenum m_target, GLenum internalformat, GLsizei width, GLsizei height); typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) (GLenum m_target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSEXTPROC) (GLsizei n, const GLuint *renderbuffers); #endif // make sure the extensions compile OK everywhere #ifndef GL_TEXTURE_STORAGE_HINT_APPLE #define GL_TEXTURE_STORAGE_HINT_APPLE 0x85bc #endif #ifndef GL_STORAGE_CACHED_APPLE #define GL_STORAGE_CACHED_APPLE 0x85be #endif #ifndef GL_UNPACK_CLIENT_STORAGE_APPLE #define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85b2 #endif #ifndef GL_TEXTURE_RECTANGLE_ARB #define GL_TEXTURE_RECTANGLE_ARB 0x84F5 #endif #ifndef GL_PIXEL_UNPACK_BUFFER_ARB #define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC #endif #ifndef GL_STREAM_DRAW #define GL_STREAM_DRAW 0x88E0 #endif #ifndef GL_WRITE_ONLY #define GL_WRITE_ONLY 0x88B9 #endif #ifndef GL_ARRAY_BUFFER_ARB #define GL_ARRAY_BUFFER_ARB 0x8892 #endif #ifndef GL_PIXEL_UNPACK_BUFFER_ARB #define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC #endif #ifndef GL_FRAMEBUFFER_EXT #define GL_FRAMEBUFFER_EXT 0x8D40 #define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 #define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 #define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 #define GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT 0x8CD8 #define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 #define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA #define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB #define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC #define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD #define GL_RENDERBUFFER_EXT 0x8D41 #define GL_DEPTH_COMPONENT16 0x81A5 #define GL_DEPTH_COMPONENT24 0x81A6 #define GL_DEPTH_COMPONENT32 0x81A7 #endif #include "drawogl.h" //============================================================ // DEBUGGING //============================================================ #define DEBUG_MODE_SCORES 0 #define USE_WIN32_STYLE_LINES 0 // use the same method baseline does - yields somewhat nicer vectors but a little buggy //============================================================ // CONSTANTS //============================================================ enum { TEXTURE_TYPE_NONE, TEXTURE_TYPE_PLAIN, TEXTURE_TYPE_DYNAMIC, TEXTURE_TYPE_SHADER, TEXTURE_TYPE_SURFACE }; //============================================================ // MACROS //============================================================ // texture formats // This used to be an enum, but these are now defines so we can use them as // preprocessor conditionals #define SDL_TEXFORMAT_ARGB32 (0) // non-16-bit textures or specials #define SDL_TEXFORMAT_RGB32 (1) #define SDL_TEXFORMAT_RGB32_PALETTED (2) #define SDL_TEXFORMAT_YUY16 (3) #define SDL_TEXFORMAT_YUY16_PALETTED (4) #define SDL_TEXFORMAT_PALETTE16 (5) #define SDL_TEXFORMAT_RGB15 (6) #define SDL_TEXFORMAT_RGB15_PALETTED (7) #define SDL_TEXFORMAT_PALETTE16A (8) #if 0 // special texture formats for 16bpp texture destination support, do not use // to address the tex properties / tex functions arrays! #define SDL_TEXFORMAT_PALETTE16_ARGB1555 (16) #define SDL_TEXFORMAT_RGB15_ARGB1555 (17) #define SDL_TEXFORMAT_RGB15_PALETTED_ARGB1555 (18) #endif #define FSWAP(var1, var2) do { float temp = var1; var1 = var2; var2 = temp; } while (0) #define GL_NO_PRIMITIVE -1 /* line_aa_step is used for drawing antialiased lines */ struct line_aa_step { float xoffs, yoffs; // X/Y deltas float weight; // weight contribution }; #if 0 static const line_aa_step line_aa_1step[] = { { 0.00f, 0.00f, 1.00f }, { 0 } }; static const line_aa_step line_aa_4step[] = { { -0.25f, 0.00f, 0.25f }, { 0.25f, 0.00f, 0.25f }, { 0.00f, -0.25f, 0.25f }, { 0.00f, 0.25f, 0.25f }, { 0 } }; #endif //============================================================ // INLINES //============================================================ HashT renderer_ogl::texture_compute_hash(const render_texinfo *texture, uint32_t flags) { HashT h = (HashT)texture->base ^ (flags & (PRIMFLAG_BLENDMODE_MASK | PRIMFLAG_TEXFORMAT_MASK)); //printf("hash %d\n", (int) h % HASH_SIZE); return (h >> 8) % HASH_SIZE; } void renderer_ogl::set_blendmode(int blendmode) { // try to minimize texture state changes if (blendmode != m_last_blendmode) { switch (blendmode) { case BLENDMODE_NONE: glDisable(GL_BLEND); break; case BLENDMODE_ALPHA: glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); break; case BLENDMODE_RGB_MULTIPLY: glEnable(GL_BLEND); glBlendFunc(GL_DST_COLOR, GL_ZERO); break; case BLENDMODE_ADD: glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); break; } m_last_blendmode = blendmode; } } //============================================================ // STATIC VARIABLES //============================================================ // OGL 1.3 #if defined(GL_ARB_multitexture) && !defined(OSD_MAC) static PFNGLACTIVETEXTUREARBPROC pfn_glActiveTexture = nullptr; #else static PFNGLACTIVETEXTUREPROC pfn_glActiveTexture = nullptr; #endif // VBO static PFNGLGENBUFFERSPROC pfn_glGenBuffers = nullptr; static PFNGLDELETEBUFFERSPROC pfn_glDeleteBuffers = nullptr; static PFNGLBINDBUFFERPROC pfn_glBindBuffer = nullptr; static PFNGLBUFFERDATAPROC pfn_glBufferData = nullptr; static PFNGLBUFFERSUBDATAPROC pfn_glBufferSubData = nullptr; // PBO static PFNGLMAPBUFFERPROC pfn_glMapBuffer = nullptr; static PFNGLUNMAPBUFFERPROC pfn_glUnmapBuffer = nullptr; // FBO static PFNGLISFRAMEBUFFEREXTPROC pfn_glIsFramebuffer = nullptr; static PFNGLBINDFRAMEBUFFEREXTPROC pfn_glBindFramebuffer = nullptr; static PFNGLDELETEFRAMEBUFFERSEXTPROC pfn_glDeleteFramebuffers = nullptr; static PFNGLGENFRAMEBUFFERSEXTPROC pfn_glGenFramebuffers = nullptr; static PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC pfn_glCheckFramebufferStatus = nullptr; static PFNGLFRAMEBUFFERTEXTURE2DEXTPROC pfn_glFramebufferTexture2D = nullptr; static int glsl_shader_feature = GLSL_SHADER_FEAT_PLAIN; //============================================================ // Textures //============================================================ static void texture_set_data(ogl_texture_info *texture, const render_texinfo *texsource, uint32_t flags); //============================================================ // Static Variables //============================================================ bool renderer_ogl::s_shown_video_info = false; bool renderer_ogl::s_dll_loaded = false; void renderer_ogl::init(running_machine &machine) { s_dll_loaded = false; load_gl_lib(machine); #if defined(OSD_WINDOWS) osd_printf_verbose("Using Windows OpenGL driver\n"); #else osd_printf_verbose("Using SDL multi-window OpenGL driver (SDL 2.0+)\n"); #endif } //============================================================ // CONSTRUCTOR & DESTRUCTOR //============================================================ renderer_ogl::~renderer_ogl() { // free the memory in the window destroy_all_textures(); global_free(m_gl_context); m_gl_context = nullptr; } void renderer_ogl::exit() { for (int i = 0; i < video_config.glsl_shader_mamebm_num; i++) { if (nullptr != video_config.glsl_shader_mamebm[i]) { free(video_config.glsl_shader_mamebm[i]); video_config.glsl_shader_mamebm[i] = nullptr; } } for (int i =0; i < video_config.glsl_shader_scrn_num; i++) { if (nullptr != video_config.glsl_shader_scrn[i]) { free(video_config.glsl_shader_scrn[i]); video_config.glsl_shader_scrn[i] = nullptr; } } } //============================================================ // Load the OGL function addresses //============================================================ static void loadgl_functions(osd_gl_context *context) { #ifdef USE_DISPATCH_GL int err_count = 0; /* the following is tricky ... #func will be expanded to glBegin * while func will be expanded to disp_p->glBegin */ #define OSD_GL(ret,func,params) \ if (!( func = (ret (APIENTRY *)params) context->getProcAddress( #func ) )) \ { err_count++; osd_printf_error("GL function %s not found!\n", #func ); } #define OSD_GL_UNUSED(ret,func,params) #define GET_GLFUNC 1 #include "modules/opengl/osd_opengl.h" #undef GET_GLFUNC if (err_count) fatalerror("Error loading GL library functions, giving up\n"); #endif } //============================================================ // Load GL library //============================================================ #ifdef USE_DISPATCH_GL osd_gl_dispatch *gl_dispatch; #endif void renderer_ogl::load_gl_lib(running_machine &machine) { if (!s_dll_loaded) { #ifndef OSD_WINDOWS #ifdef USE_DISPATCH_GL /* * directfb and and x11 use this env var * SDL_VIDEO_GL_DRIVER */ const char *stemp; stemp = downcast(machine.options()).gl_lib(); if (stemp != nullptr && strcmp(stemp, OSDOPTVAL_AUTO) == 0) stemp = nullptr; if (SDL_GL_LoadLibrary(stemp) != 0) // Load library (default for e==nullptr { fatalerror("Unable to load opengl library: %s\n", stemp ? stemp : ""); } osd_printf_verbose("Loaded opengl shared library: %s\n", stemp ? stemp : ""); /* FIXME: must be freed as well */ #endif #endif #ifdef USE_DISPATCH_GL gl_dispatch = global_alloc(osd_gl_dispatch); #endif s_dll_loaded = true; } } void renderer_ogl::initialize_gl() { int has_and_allow_texturerect = 0; char *extstr = (char *)glGetString(GL_EXTENSIONS); char *vendor = (char *)glGetString(GL_VENDOR); //printf("%p\n", extstr); #ifdef OSD_WINDOWS if (!extstr) extstr = (char *)""; #endif // print out the driver info for debugging if (!s_shown_video_info) { osd_printf_verbose("OpenGL: %s\nOpenGL: %s\nOpenGL: %s\n", vendor, (char *)glGetString(GL_RENDERER), (char *)glGetString(GL_VERSION)); } m_usetexturerect = 0; m_texpoweroftwo = 1; m_usevbo = 0; m_usepbo = 0; m_usefbo = 0; m_useglsl = 0; if (video_config.allowtexturerect && (strstr(extstr, "GL_ARB_texture_rectangle") || strstr(extstr, "GL_EXT_texture_rectangle"))) { has_and_allow_texturerect = 1; if (!s_shown_video_info) { osd_printf_verbose("OpenGL: texture rectangle supported\n"); } } // does this card support non-power-of-two sized textures? (they're faster, so use them if possible) if ( !video_config.forcepow2texture && strstr(extstr, "GL_ARB_texture_non_power_of_two")) { if (!s_shown_video_info) { osd_printf_verbose("OpenGL: non-power-of-2 textures supported (new method)\n"); } m_texpoweroftwo = 0; } else { // second chance: GL_ARB_texture_rectangle or GL_EXT_texture_rectangle (old version) if (has_and_allow_texturerect) { if (!s_shown_video_info) { osd_printf_verbose("OpenGL: non-power-of-2 textures supported (old method)\n"); } m_usetexturerect = 1; } else { if (!s_shown_video_info) { osd_printf_verbose("OpenGL: forcing power-of-2 textures (creation, not copy)\n"); } } } if (strstr(extstr, "GL_ARB_vertex_buffer_object")) { m_usevbo = video_config.vbo; if (!s_shown_video_info) { if(m_usevbo) osd_printf_verbose("OpenGL: vertex buffer supported\n"); else osd_printf_verbose("OpenGL: vertex buffer supported, but disabled\n"); } } if (strstr(extstr, "GL_ARB_pixel_buffer_object")) { if( m_usevbo ) { m_usepbo = video_config.pbo; if (!s_shown_video_info) { if(m_usepbo) osd_printf_verbose("OpenGL: pixel buffers supported\n"); else osd_printf_verbose("OpenGL: pixel buffers supported, but disabled\n"); } } else { if (!s_shown_video_info) { osd_printf_verbose("OpenGL: pixel buffers supported, but disabled due to disabled vbo\n"); } } } else { if (!s_shown_video_info) { osd_printf_verbose("OpenGL: pixel buffers not supported\n"); } } if (strstr(extstr, "GL_EXT_framebuffer_object")) { m_usefbo = 1; if (!s_shown_video_info) { if(m_usefbo) osd_printf_verbose("OpenGL: framebuffer object supported\n"); else osd_printf_verbose("OpenGL: framebuffer object not supported\n"); } } if (strstr(extstr, "GL_ARB_shader_objects") && strstr(extstr, "GL_ARB_shading_language_100") && strstr(extstr, "GL_ARB_vertex_shader") && strstr(extstr, "GL_ARB_fragment_shader") ) { m_useglsl = video_config.glsl; if (!s_shown_video_info) { if(m_useglsl) osd_printf_verbose("OpenGL: GLSL supported\n"); else osd_printf_verbose("OpenGL: GLSL supported, but disabled\n"); } } else { if (!s_shown_video_info) { osd_printf_verbose("OpenGL: GLSL not supported\n"); } } #ifdef TOBEMIGRATED if (osd_getenv(SDLENV_VMWARE) != nullptr) { m_usetexturerect = 1; m_texpoweroftwo = 1; } #endif glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint *)&m_texture_max_width); glGetIntegerv(GL_MAX_TEXTURE_SIZE, (GLint *)&m_texture_max_height); if (!s_shown_video_info) { osd_printf_verbose("OpenGL: max texture size %d x %d\n", m_texture_max_width, m_texture_max_height); } s_shown_video_info = true; } //============================================================ // sdl_info::create //============================================================ int renderer_ogl::create() { auto win = assert_window(); // create renderer #if defined(OSD_WINDOWS) m_gl_context = global_alloc(win_gl_context(std::static_pointer_cast(win)->platform_window())); #elif defined(OSD_MAC) // TODO // m_gl_context = global_alloc(mac_gl_context(std::static_pointer_cast(win)->platform_window())); #else m_gl_context = global_alloc(sdl_gl_context(std::static_pointer_cast(win)->platform_window())); #endif if (m_gl_context->LastErrorMsg() != nullptr) { osd_printf_error("%s\n", m_gl_context->LastErrorMsg()); return 1; } m_gl_context->SetSwapInterval(video_config.waitvsync ? 1 : 0); m_blittimer = 0; m_surf_w = 0; m_surf_h = 0; m_initialized = 0; // in case any textures try to come up before these are validated, // OpenGL guarantees all implementations can handle something this size. m_texture_max_width = 64; m_texture_max_height = 64; /* load any GL function addresses * this must be done here because we need a context */ loadgl_functions(m_gl_context); initialize_gl(); m_init_context = 0; osd_printf_verbose("Leave renderer_ogl::create\n"); return 0; } //============================================================ // drawsdl_xy_to_render_target //============================================================ #ifndef OSD_WINDOWS int renderer_ogl::xy_to_render_target(int x, int y, int *xt, int *yt) { *xt = x - m_last_hofs; *yt = y - m_last_vofs; if (*xt<0 || *xt >= m_blit_dim.width()) return 0; if (*yt<0 || *yt >= m_blit_dim.height()) return 0; return 1; } #endif //============================================================ // renderer_ogl::destroy_all_textures //============================================================ void renderer_ogl::destroy_all_textures() { ogl_texture_info *texture = nullptr; bool lock=false; int i; if ( !m_initialized ) return; auto win = try_getwindow(); // During destroy this can get called // and the window is no longer available if (win == nullptr) return; m_gl_context->MakeCurrent(); if(win->m_primlist) { lock=true; win->m_primlist->acquire_lock(); } glFinish(); texture_all_disable(); glFinish(); glDisableClientState(GL_VERTEX_ARRAY); i=0; while (itexCoordBufferName) ); texture->texCoordBufferName=0; } if(m_usepbo && texture->pbo) { pfn_glDeleteBuffers( 1, (GLuint *)&(texture->pbo) ); texture->pbo=0; } if( m_glsl_program_num > 1 ) { assert(m_usefbo); pfn_glDeleteFramebuffers(2, (GLuint *)&texture->mpass_fbo_mamebm[0]); glDeleteTextures(2, (GLuint *)&texture->mpass_texture_mamebm[0]); } if ( m_glsl_program_mb2sc < m_glsl_program_num - 1 ) { assert(m_usefbo); pfn_glDeleteFramebuffers(2, (GLuint *)&texture->mpass_fbo_scrn[0]); glDeleteTextures(2, (GLuint *)&texture->mpass_texture_scrn[0]); } glDeleteTextures(1, (GLuint *)&texture->texture); if ( texture->data_own ) { free(texture->data); texture->data=nullptr; texture->data_own=false; } global_free(texture); } i++; } if ( m_useglsl ) { glsl_shader_free(m_glsl); m_glsl = nullptr; } m_initialized = 0; if (lock) win->m_primlist->release_lock(); } //============================================================ // loadGLExtensions //============================================================ void renderer_ogl::loadGLExtensions() { static int _once = 1; // usevbo=false; // You may want to switch VBO and PBO off, by uncommenting this statement // usepbo=false; // You may want to switch PBO off, by uncommenting this statement // useglsl=false; // You may want to switch GLSL off, by uncommenting this statement if (! m_usevbo) { if(m_usepbo) // should never ever happen ;-) { if (_once) { osd_printf_warning("OpenGL: PBO not supported, no VBO support. (sdlmame error)\n"); } m_usepbo=false; } if(m_useglsl) // should never ever happen ;-) { if (_once) { osd_printf_warning("OpenGL: GLSL not supported, no VBO support. (sdlmame error)\n"); } m_useglsl=false; } } // Get Pointers To The GL Functions // VBO: if( m_usevbo ) { pfn_glGenBuffers = (PFNGLGENBUFFERSPROC) m_gl_context->getProcAddress("glGenBuffers"); pfn_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC) m_gl_context->getProcAddress("glDeleteBuffers"); pfn_glBindBuffer = (PFNGLBINDBUFFERPROC) m_gl_context->getProcAddress("glBindBuffer"); pfn_glBufferData = (PFNGLBUFFERDATAPROC) m_gl_context->getProcAddress("glBufferData"); pfn_glBufferSubData = (PFNGLBUFFERSUBDATAPROC) m_gl_context->getProcAddress("glBufferSubData"); } // PBO: if ( m_usepbo ) { pfn_glMapBuffer = (PFNGLMAPBUFFERPROC) m_gl_context->getProcAddress("glMapBuffer"); pfn_glUnmapBuffer= (PFNGLUNMAPBUFFERPROC) m_gl_context->getProcAddress("glUnmapBuffer"); } // FBO: if ( m_usefbo ) { pfn_glIsFramebuffer = (PFNGLISFRAMEBUFFEREXTPROC) m_gl_context->getProcAddress("glIsFramebufferEXT"); pfn_glBindFramebuffer = (PFNGLBINDFRAMEBUFFEREXTPROC) m_gl_context->getProcAddress("glBindFramebufferEXT"); pfn_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSEXTPROC) m_gl_context->getProcAddress("glDeleteFramebuffersEXT"); pfn_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSEXTPROC) m_gl_context->getProcAddress("glGenFramebuffersEXT"); pfn_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) m_gl_context->getProcAddress("glCheckFramebufferStatusEXT"); pfn_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) m_gl_context->getProcAddress("glFramebufferTexture2DEXT"); } if ( m_usevbo && ( !pfn_glGenBuffers || !pfn_glDeleteBuffers || !pfn_glBindBuffer || !pfn_glBufferData || !pfn_glBufferSubData ) ) { m_usepbo=false; if (_once) { osd_printf_warning("OpenGL: VBO not supported, missing: "); if (!pfn_glGenBuffers) { osd_printf_warning("glGenBuffers, "); } if (!pfn_glDeleteBuffers) { osd_printf_warning("glDeleteBuffers"); } if (!pfn_glBindBuffer) { osd_printf_warning("glBindBuffer, "); } if (!pfn_glBufferData) { osd_printf_warning("glBufferData, "); } if (!pfn_glBufferSubData) { osd_printf_warning("glBufferSubData, "); } osd_printf_warning("\n"); } if ( m_usevbo ) { if (_once) { osd_printf_warning("OpenGL: PBO not supported, no VBO support.\n"); } m_usepbo=false; } } if ( m_usepbo && ( !pfn_glMapBuffer || !pfn_glUnmapBuffer ) ) { m_usepbo=false; if (_once) { osd_printf_warning("OpenGL: PBO not supported, missing: "); if (!pfn_glMapBuffer) { osd_printf_warning("glMapBuffer, "); } if (!pfn_glUnmapBuffer) { osd_printf_warning("glUnmapBuffer, "); } osd_printf_warning("\n"); } } if ( m_usefbo && ( !pfn_glIsFramebuffer || !pfn_glBindFramebuffer || !pfn_glDeleteFramebuffers || !pfn_glGenFramebuffers || !pfn_glCheckFramebufferStatus || !pfn_glFramebufferTexture2D )) { m_usefbo=false; if (_once) { osd_printf_warning("OpenGL: FBO not supported, missing: "); if (!pfn_glIsFramebuffer) { osd_printf_warning("pfn_glIsFramebuffer, "); } if (!pfn_glBindFramebuffer) { osd_printf_warning("pfn_glBindFramebuffer, "); } if (!pfn_glDeleteFramebuffers) { osd_printf_warning("pfn_glDeleteFramebuffers, "); } if (!pfn_glGenFramebuffers) { osd_printf_warning("pfn_glGenFramebuffers, "); } if (!pfn_glCheckFramebufferStatus) { osd_printf_warning("pfn_glCheckFramebufferStatus, "); } if (!pfn_glFramebufferTexture2D) { osd_printf_warning("pfn_glFramebufferTexture2D, "); } osd_printf_warning("\n"); } } if (_once) { if ( m_usevbo ) { osd_printf_verbose("OpenGL: VBO supported\n"); } else { osd_printf_warning("OpenGL: VBO not supported\n"); } if ( m_usepbo ) { osd_printf_verbose("OpenGL: PBO supported\n"); } else { osd_printf_warning("OpenGL: PBO not supported\n"); } if ( m_usefbo ) { osd_printf_verbose("OpenGL: FBO supported\n"); } else { osd_printf_warning("OpenGL: FBO not supported\n"); } } if ( m_useglsl ) { #if defined(GL_ARB_multitexture) && !defined(OSD_MAC) pfn_glActiveTexture = (PFNGLACTIVETEXTUREARBPROC) m_gl_context->getProcAddress("glActiveTextureARB"); #else pfn_glActiveTexture = (PFNGLACTIVETEXTUREPROC) m_gl_context->getProcAddress("glActiveTexture"); #endif if (!pfn_glActiveTexture) { if (_once) { osd_printf_warning("OpenGL: GLSL disabled, glActiveTexture(ARB) not supported\n"); } m_useglsl = 0; } } if ( m_useglsl ) { m_glsl = glsl_shader_init(m_gl_context); m_useglsl = (m_glsl != nullptr ? 1 : 0); if ( ! m_useglsl ) { if (_once) { osd_printf_warning("OpenGL: GLSL supported, but shader instantiation failed - disabled\n"); } } } if ( m_useglsl ) { if (assert_window()->prescale() != 1 ) { m_useglsl = 0; if (_once) { osd_printf_warning("OpenGL: GLSL supported, but disabled due to: prescale !=1 \n"); } } } if ( m_useglsl ) { int i; video_config.filter = false; glsl_shader_feature = GLSL_SHADER_FEAT_PLAIN; m_glsl_program_num = 0; m_glsl_program_mb2sc = 0; for(i=0; i 0 && m_glsl_program_num==0 ) { osd_printf_verbose("OpenGL: GLSL cannot use screen bitmap shader without bitmap shader\n"); } for(i=0; m_usefbo && m_glsl_program_num>0 && iget_size(); if (has_flags(FI_CHANGED) || (wdim.width() != m_width) || (wdim.height() != m_height)) { destroy_all_textures(); m_width = wdim.width(); m_height = wdim.height(); m_blittimer = 3; m_init_context = 1; clear_flags(FI_CHANGED); } m_gl_context->MakeCurrent(); if (m_init_context) { // do some one-time OpenGL setup // FIXME: SRGB conversion is working on SDL2, may be of use // when we eventually target gamma and monitor profiles. //glEnable(GL_FRAMEBUFFER_SRGB); glShadeModel(GL_SMOOTH); glClearColor(0.0f, 0.0f, 0.0f, 0.0f); glClearDepth(1.0f); glEnable(GL_DEPTH_TEST); glDepthFunc(GL_LEQUAL); glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST); } // only clear if the geometry changes (and for 2 frames afterward to clear double and triple buffers) if ((m_blittimer > 0) || has_flags(FLAG_HAS_VECTOR_SCREEN)) { glClear(GL_COLOR_BUFFER_BIT); m_blittimer--; } // FIXME: remove m_surf_w and m_surf_h if ( !m_initialized || m_width != m_surf_w || m_height != m_surf_h ) { // FIXME:: this can be done in create! if ( !m_initialized ) { loadGLExtensions(); } m_surf_w = m_width; m_surf_h = m_height; // we're doing nothing 3d, so the Z-buffer is currently not interesting glDisable(GL_DEPTH_TEST); // enable antialiasing for lines glEnable(GL_LINE_SMOOTH); // enable antialiasing for points glEnable(GL_POINT_SMOOTH); // prefer quality to speed glHint(GL_POINT_SMOOTH_HINT, GL_NICEST); glHint(GL_LINE_SMOOTH_HINT, GL_NICEST); // enable blending glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); m_last_blendmode = BLENDMODE_ALPHA; #ifdef TOBEMIGRATED // set lines and points just barely above normal size to get proper results glLineWidth(video_config.beamwidth); glPointSize(video_config.beamwidth); #endif // set up a nice simple 2D coordinate system, so GL behaves exactly how we'd like. // // (0,0) (w,0) // |~~~~~~~~~| // | | // | | // | | // |_________| // (0,h) (w,h) GLsizei iScale = 1; /* Mac hack: macOS version 10.15 and later flipped from assuming you don't support Retina to assuming you do support Retina. SDL 2.0.11 is scheduled to fix this, but it's not out yet. So we double-scale everything if you're on 10.15 or later and SDL is not at least version 2.0.11. */ #if defined(SDLMAME_MACOSX) || defined(OSD_MAC) SDL_version sdlVers; SDL_GetVersion(&sdlVers); // Only do this if SDL is not at least 2.0.11. if ((sdlVers.major == 2) && (sdlVers.minor == 0) && (sdlVers.patch < 11)) { // now get the Darwin kernel version int dMaj, dMin, dPatch; char versStr[64]; dMaj = dMin = dPatch = 0; size_t size = sizeof(versStr); int retVal = sysctlbyname("kern.osrelease", versStr, &size, NULL, 0); if (retVal == 0) { sscanf(versStr, "%d.%d.%d", &dMaj, &dMin, &dPatch); // 10.15 Catalina is Darwin version 19 if (dMaj >= 19) { // do the workaround for Retina being forced on osd_printf_verbose("OpenGL: enabling Retina workaround\n"); iScale = 2; } } } #endif glViewport(0.0, 0.0, (GLsizei) m_width * iScale, (GLsizei) m_height * iScale); glMatrixMode(GL_PROJECTION); glLoadIdentity(); glOrtho(0.0, (GLdouble) m_width, (GLdouble) m_height, 0.0, 0.0, -1.0); glMatrixMode(GL_MODELVIEW); glLoadIdentity(); if ( ! m_initialized ) { glEnableClientState(GL_VERTEX_ARRAY); glVertexPointer(2, GL_FLOAT, 0, m_texVerticex); // no VBO, since it's too volatile m_initialized = 1; } } // compute centering parameters vofs = hofs = 0.0f; #ifdef TOBEMIGRATED if (video_config.centerv || video_config.centerh) { int ch, cw; ch = m_height; cw = m_width; if (video_config.centerv) { vofs = (ch - m_blit_dim.height()) / 2.0f; } if (video_config.centerh) { hofs = (cw - m_blit_dim.width()) / 2.0f; } } #else #endif m_last_hofs = hofs; m_last_vofs = vofs; win->m_primlist->acquire_lock(); // now draw for (render_primitive &prim : *win->m_primlist) { int i; switch (prim.type) { /** * Try to stay in one Begin/End block as long as possible, * since entering and leaving one is most expensive.. */ case render_primitive::LINE: #if !USE_WIN32_STYLE_LINES // check if it's really a point if (((prim.bounds.x1 - prim.bounds.x0) == 0) && ((prim.bounds.y1 - prim.bounds.y0) == 0)) { curPrimitive=GL_POINTS; } else { curPrimitive=GL_LINES; } if(pendingPrimitive!=GL_NO_PRIMITIVE && pendingPrimitive!=curPrimitive) { glEnd(); pendingPrimitive=GL_NO_PRIMITIVE; } if ( pendingPrimitive==GL_NO_PRIMITIVE ) { set_blendmode(PRIMFLAG_GET_BLENDMODE(prim.flags)); } glColor4f(prim.color.r, prim.color.g, prim.color.b, prim.color.a); if(pendingPrimitive!=curPrimitive) { glBegin(curPrimitive); pendingPrimitive=curPrimitive; } // check if it's really a point if (curPrimitive==GL_POINTS) { glVertex2f(prim.bounds.x0+hofs, prim.bounds.y0+vofs); } else { glVertex2f(prim.bounds.x0+hofs, prim.bounds.y0+vofs); glVertex2f(prim.bounds.x1+hofs, prim.bounds.y1+vofs); } #else { const line_aa_step *step = line_aa_4step; render_bounds b0, b1; float r, g, b, a; float effwidth; // we're not gonna play fancy here. close anything pending and let's go. if (pendingPrimitive!=GL_NO_PRIMITIVE && pendingPrimitive!=curPrimitive) { glEnd(); pendingPrimitive=GL_NO_PRIMITIVE; } set_blendmode(sdl, PRIMFLAG_GET_BLENDMODE(prim.flags)); // compute the effective width based on the direction of the line effwidth = prim.width(); if (effwidth < 0.5f) effwidth = 0.5f; // determine the bounds of a quad to draw this line render_line_to_quad(&prim.bounds, effwidth, 0.0f, &b0, &b1); // fix window position b0.x0 += hofs; b0.x1 += hofs; b1.x0 += hofs; b1.x1 += hofs; b0.y0 += vofs; b0.y1 += vofs; b1.y0 += vofs; b1.y1 += vofs; // iterate over AA steps for (step = PRIMFLAG_GET_ANTIALIAS(prim.flags) ? line_aa_4step : line_aa_1step; step->weight != 0; step++) { glBegin(GL_TRIANGLE_STRIP); // rotate the unit vector by 135 degrees and add to point 0 glVertex2f(b0.x0 + step->xoffs, b0.y0 + step->yoffs); // rotate the unit vector by -135 degrees and add to point 0 glVertex2f(b0.x1 + step->xoffs, b0.y1 + step->yoffs); // rotate the unit vector by 45 degrees and add to point 1 glVertex2f(b1.x0 + step->xoffs, b1.y0 + step->yoffs); // rotate the unit vector by -45 degrees and add to point 1 glVertex2f(b1.x1 + step->xoffs, b1.y1 + step->yoffs); // determine the color of the line r = (prim.color.r * step->weight); g = (prim.color.g * step->weight); b = (prim.color.b * step->weight); a = (prim.color.a * 255.0f); if (r > 1.0) r = 1.0; if (g > 1.0) g = 1.0; if (b > 1.0) b = 1.0; if (a > 1.0) a = 1.0; glColor4f(r, g, b, a); // texture = texture_update(window, &prim, 0); // if (texture) printf("line has texture!\n"); // if we have a texture to use for the vectors, use it here #if 0 if (d3d->vector_texture != nullptr) { printf("SDL: textured lines unsupported\n"); vertex[0].u0 = d3d->vector_texture->ustart; vertex[0].v0 = d3d->vector_texture->vstart; vertex[2].u0 = d3d->vector_texture->ustop; vertex[2].v0 = d3d->vector_texture->vstart; vertex[1].u0 = d3d->vector_texture->ustart; vertex[1].v0 = d3d->vector_texture->vstop; vertex[3].u0 = d3d->vector_texture->ustop; vertex[3].v0 = d3d->vector_texture->vstop; } #endif glEnd(); } } #endif break; case render_primitive::QUAD: if(pendingPrimitive!=GL_NO_PRIMITIVE) { glEnd(); pendingPrimitive=GL_NO_PRIMITIVE; } glColor4f(prim.color.r, prim.color.g, prim.color.b, prim.color.a); set_blendmode(PRIMFLAG_GET_BLENDMODE(prim.flags)); texture = texture_update(&prim, 0); if ( texture && texture->type==TEXTURE_TYPE_SHADER ) { for(i=0; i transformation mamebm->scrn m_texVerticex[0]=prim.bounds.x0 + hofs; m_texVerticex[1]=prim.bounds.y0 + vofs; m_texVerticex[2]=prim.bounds.x1 + hofs; m_texVerticex[3]=prim.bounds.y0 + vofs; m_texVerticex[4]=prim.bounds.x1 + hofs; m_texVerticex[5]=prim.bounds.y1 + vofs; m_texVerticex[6]=prim.bounds.x0 + hofs; m_texVerticex[7]=prim.bounds.y1 + vofs; } else { // 1:1 tex coord CCW (0/0) (1/0) (1/1) (0/1) on texture dimensions m_texVerticex[0]=(GLfloat)0.0; m_texVerticex[1]=(GLfloat)0.0; m_texVerticex[2]=(GLfloat)m_width; m_texVerticex[3]=(GLfloat)0.0; m_texVerticex[4]=(GLfloat)m_width; m_texVerticex[5]=(GLfloat)m_height; m_texVerticex[6]=(GLfloat)0.0; m_texVerticex[7]=(GLfloat)m_height; } if(i>0) // first fetch already done { texture = texture_update(&prim, i); } glDrawArrays(GL_QUADS, 0, 4); } } else { m_texVerticex[0]=prim.bounds.x0 + hofs; m_texVerticex[1]=prim.bounds.y0 + vofs; m_texVerticex[2]=prim.bounds.x1 + hofs; m_texVerticex[3]=prim.bounds.y0 + vofs; m_texVerticex[4]=prim.bounds.x1 + hofs; m_texVerticex[5]=prim.bounds.y1 + vofs; m_texVerticex[6]=prim.bounds.x0 + hofs; m_texVerticex[7]=prim.bounds.y1 + vofs; glDrawArrays(GL_QUADS, 0, 4); } if ( texture ) { texture_disable(texture); texture=nullptr; } break; default: throw emu_fatalerror("Unexpected render_primitive type"); } } if(pendingPrimitive!=GL_NO_PRIMITIVE) { glEnd(); pendingPrimitive=GL_NO_PRIMITIVE; } win->m_primlist->release_lock(); m_init_context = 0; m_gl_context->SwapBuffer(); return 0; } //============================================================ // texture handling //============================================================ static const char * texfmt_to_string[9] = { "ARGB32", "RGB32", "RGB32_PALETTED", "YUV16", "YUV16_PALETTED", "PALETTE16", "RGB15", "RGB15_PALETTE", "PALETTE16A" }; // // Note: if you change the following array order, change the matching defines in texsrc.h // enum { SDL_TEXFORMAT_SRC_EQUALS_DEST, SDL_TEXFORMAT_SRC_HAS_PALETTE }; static const GLint texture_copy_properties[9][2] = { { true, false }, // SDL_TEXFORMAT_ARGB32 { true, false }, // SDL_TEXFORMAT_RGB32 { true, true }, // SDL_TEXFORMAT_RGB32_PALETTED { false, false }, // SDL_TEXFORMAT_YUY16 { false, true }, // SDL_TEXFORMAT_YUY16_PALETTED { false, true }, // SDL_TEXFORMAT_PALETTE16 { true, false }, // SDL_TEXFORMAT_RGB15 { true, true }, // SDL_TEXFORMAT_RGB15_PALETTED { false, true } // SDL_TEXFORMAT_PALETTE16A }; //============================================================ // texture_compute_size and type //============================================================ // // glBufferData to push a nocopy texture to the GPU is slower than TexSubImage2D, // so don't use PBO here // // we also don't want to use PBO's in the case of nocopy==true, // since we now might have GLSL shaders - this decision simplifies out life ;-) // void renderer_ogl::texture_compute_type_subroutine(const render_texinfo *texsource, ogl_texture_info *texture, uint32_t flags) { texture->type = TEXTURE_TYPE_NONE; texture->nocopy = false; if ( texture->type == TEXTURE_TYPE_NONE && !PRIMFLAG_GET_SCREENTEX(flags)) { texture->type = TEXTURE_TYPE_PLAIN; texture->texTarget = (m_usetexturerect)?GL_TEXTURE_RECTANGLE_ARB:GL_TEXTURE_2D; texture->texpow2 = (m_usetexturerect)?0:m_texpoweroftwo; } if ( texture->type == TEXTURE_TYPE_NONE && m_useglsl && texture->xprescale == 1 && texture->yprescale == 1 && texsource->rowpixels <= m_texture_max_width ) { texture->type = TEXTURE_TYPE_SHADER; texture->texTarget = GL_TEXTURE_2D; texture->texpow2 = m_texpoweroftwo; } // determine if we can skip the copy step // if this was not already decided by the shader condition above if ( texture_copy_properties[texture->format][SDL_TEXFORMAT_SRC_EQUALS_DEST] && !texture_copy_properties[texture->format][SDL_TEXFORMAT_SRC_HAS_PALETTE] && texture->xprescale == 1 && texture->yprescale == 1 && !texture->borderpix && !texsource->palette && texsource->rowpixels <= m_texture_max_width ) { texture->nocopy = true; } if( texture->type == TEXTURE_TYPE_NONE && m_usepbo && !texture->nocopy ) { texture->type = TEXTURE_TYPE_DYNAMIC; texture->texTarget = (m_usetexturerect)?GL_TEXTURE_RECTANGLE_ARB:GL_TEXTURE_2D; texture->texpow2 = (m_usetexturerect)?0:m_texpoweroftwo; } if( texture->type == TEXTURE_TYPE_NONE ) { texture->type = TEXTURE_TYPE_SURFACE; texture->texTarget = (m_usetexturerect)?GL_TEXTURE_RECTANGLE_ARB:GL_TEXTURE_2D; texture->texpow2 = (m_usetexturerect)?0:m_texpoweroftwo; } } static inline int get_valid_pow2_value(int v, int needPow2) { return (needPow2)?gl_round_to_pow2(v):v; } void renderer_ogl::texture_compute_size_subroutine(ogl_texture_info *texture, uint32_t flags, uint32_t width, uint32_t height, int* p_width, int* p_height, int* p_width_create, int* p_height_create) { int width_create; int height_create; if ( texture->texpow2 ) { width_create = gl_round_to_pow2 (width); height_create = gl_round_to_pow2 (height); } else if ( texture->type==TEXTURE_TYPE_SHADER ) { /** * at least use a multiple of 8 for shader .. just in case */ width_create = ( width & ~0x07 ) + ( (width & 0x07)? 8 : 0 ) ; height_create = ( height & ~0x07 ) + ( (height & 0x07)? 8 : 0 ) ; } else { width_create = width ; height_create = height ; } // don't prescale above max texture size while (texture->xprescale > 1 && width_create * texture->xprescale > m_texture_max_width) texture->xprescale--; while (texture->yprescale > 1 && height_create * texture->yprescale > m_texture_max_height) texture->yprescale--; auto win = assert_window(); if (PRIMFLAG_GET_SCREENTEX(flags) && (texture->xprescale != win->prescale() || texture->yprescale != win->prescale())) osd_printf_warning("SDL: adjusting prescale from %dx%d to %dx%d\n", win->prescale(), win->prescale(), texture->xprescale, texture->yprescale); width *= texture->xprescale; height *= texture->yprescale; width_create *= texture->xprescale; height_create *= texture->yprescale; // adjust the size for the border (must do this *after* the power of 2 clamp to satisfy // OpenGL semantics) if (texture->borderpix) { width += 2; height += 2; width_create += 2; height_create += 2; } *p_width=width; *p_height=height; *p_width_create=width_create; *p_height_create=height_create; } void renderer_ogl::texture_compute_size_type(const render_texinfo *texsource, ogl_texture_info *texture, uint32_t flags) { int finalheight, finalwidth; int finalheight_create, finalwidth_create; // if we're not wrapping, add a 1 pixel border on all sides texture->borderpix = 0; //!(texture->flags & PRIMFLAG_TEXWRAP_MASK); if (PRIMFLAG_GET_SCREENTEX(flags)) { texture->borderpix = 0; // don't border the screen right now, there's a bug } texture_compute_type_subroutine(texsource, texture, flags); texture_compute_size_subroutine(texture, flags, texsource->width, texsource->height, &finalwidth, &finalheight, &finalwidth_create, &finalheight_create); // if we added pixels for the border, and that just barely pushed us over, take it back if (texture->borderpix && ((finalwidth > m_texture_max_width && finalwidth - 2 <= m_texture_max_width) || (finalheight > m_texture_max_height && finalheight - 2 <= m_texture_max_height))) { texture->borderpix = false; texture_compute_type_subroutine(texsource, texture, flags); texture_compute_size_subroutine(texture, flags, texsource->width, texsource->height, &finalwidth, &finalheight, &finalwidth_create, &finalheight_create); } // if we're above the max width/height, do what? if (finalwidth_create > m_texture_max_width || finalheight_create > m_texture_max_height) { static int printed = false; if (!printed) osd_printf_warning("Texture too big! (wanted: %dx%d, max is %dx%d)\n", finalwidth_create, finalheight_create, m_texture_max_width, m_texture_max_height); printed = true; } if(!texture->nocopy || texture->type==TEXTURE_TYPE_DYNAMIC || texture->type==TEXTURE_TYPE_SHADER || // any of the mame core's device generated bitmap types: texture->format==SDL_TEXFORMAT_RGB32 || texture->format==SDL_TEXFORMAT_RGB32_PALETTED || texture->format==SDL_TEXFORMAT_RGB15 || texture->format==SDL_TEXFORMAT_RGB15_PALETTED || texture->format==SDL_TEXFORMAT_PALETTE16 || texture->format==SDL_TEXFORMAT_PALETTE16A ) { osd_printf_verbose("GL texture: copy %d, shader %d, dynamic %d, %dx%d %dx%d [%s, Equal: %d, Palette: %d,\n" " scale %dx%d, border %d, pitch %d,%d/%d], bytes/pix %d\n", !texture->nocopy, texture->type==TEXTURE_TYPE_SHADER, texture->type==TEXTURE_TYPE_DYNAMIC, finalwidth, finalheight, finalwidth_create, finalheight_create, texfmt_to_string[texture->format], (int)texture_copy_properties[texture->format][SDL_TEXFORMAT_SRC_EQUALS_DEST], (int)texture_copy_properties[texture->format][SDL_TEXFORMAT_SRC_HAS_PALETTE], texture->xprescale, texture->yprescale, texture->borderpix, texsource->rowpixels, finalwidth, m_texture_max_width, (int)sizeof(uint32_t) ); } // set the final values texture->rawwidth = finalwidth; texture->rawheight = finalheight; texture->rawwidth_create = finalwidth_create; texture->rawheight_create = finalheight_create; } //============================================================ // texture_create //============================================================ static int gl_checkFramebufferStatus(void) { GLenum status; status=(GLenum)pfn_glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT); switch(status) { case GL_FRAMEBUFFER_COMPLETE_EXT: return 0; case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: osd_printf_error("GL FBO: incomplete,incomplete attachment\n"); return -1; case GL_FRAMEBUFFER_UNSUPPORTED_EXT: osd_printf_error("GL FBO: Unsupported framebuffer format\n"); return -1; case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: osd_printf_error("GL FBO: incomplete,missing attachment\n"); return -1; case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: osd_printf_error("GL FBO: incomplete,attached images must have same dimensions\n"); return -1; case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: osd_printf_error("GL FBO: incomplete,attached images must have same format\n"); return -1; case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: osd_printf_error("GL FBO: incomplete,missing draw buffer\n"); return -1; case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: osd_printf_error("GL FBO: incomplete,missing read buffer\n"); return -1; #ifdef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT: osd_printf_error("GL FBO: incomplete, duplicate attachment\n"); return -1; #endif case 0: osd_printf_error("GL FBO: incomplete, implementation fault\n"); return -1; default: osd_printf_error("GL FBO: incomplete, implementation ERROR\n"); /* fall through */ } return -1; } static int texture_fbo_create(uint32_t text_unit, uint32_t text_name, uint32_t fbo_name, int width, int height) { pfn_glActiveTexture(text_unit); pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo_name); glBindTexture(GL_TEXTURE_2D, text_name); { GLint _width, _height; if ( gl_texture_check_size(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, &_width, &_height, 1) ) { osd_printf_error("cannot create fbo texture, req: %dx%d, avail: %dx%d - bail out\n", width, height, (int)_width, (int)_height); return -1; } glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr ); } // non-screen textures will never be filtered glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); pfn_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, text_name, 0); if ( gl_checkFramebufferStatus() ) { osd_printf_error("FBO error fbo texture - bail out\n"); return -1; } return 0; } int renderer_ogl::texture_shader_create(const render_texinfo *texsource, ogl_texture_info *texture, uint32_t flags) { int uniform_location; int i; int surf_w_pow2 = get_valid_pow2_value (m_blit_dim.width(), texture->texpow2); int surf_h_pow2 = get_valid_pow2_value (m_blit_dim.height(), texture->texpow2); assert ( texture->type==TEXTURE_TYPE_SHADER ); GL_CHECK_ERROR_QUIET(); if( m_glsl_program_num > 1 ) { // multipass mode assert(m_usefbo); // GL_TEXTURE3 GLSL Uniforms texture->mpass_dest_idx = 0; texture->mpass_textureunit[0] = GL_TEXTURE3; texture->mpass_textureunit[1] = GL_TEXTURE2; } for(i=0; irawwidth, (GLfloat)texture->rawheight }; uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[i], "color_texture_sz"); pfn_glUniform2fvARB(uniform_location, 1, &(color_texture_sz[0])); GL_CHECK_ERROR_NORMAL(); } GLfloat color_texture_pow2_sz[2] = { (GLfloat)texture->rawwidth_create, (GLfloat)texture->rawheight_create }; uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[i], "color_texture_pow2_sz"); pfn_glUniform2fvARB(uniform_location, 1, &(color_texture_pow2_sz[0])); GL_CHECK_ERROR_NORMAL(); GLfloat screen_texture_sz[2] = { (GLfloat) m_blit_dim.width(), (GLfloat) m_blit_dim.height() }; uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[i], "screen_texture_sz"); pfn_glUniform2fvARB(uniform_location, 1, &(screen_texture_sz[0])); GL_CHECK_ERROR_NORMAL(); GLfloat screen_texture_pow2_sz[2] = { (GLfloat)surf_w_pow2, (GLfloat)surf_h_pow2 }; uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[i], "screen_texture_pow2_sz"); pfn_glUniform2fvARB(uniform_location, 1, &(screen_texture_pow2_sz[0])); GL_CHECK_ERROR_NORMAL(); } pfn_glUseProgramObjectARB(m_glsl_program[0]); // start with 1st shader if( m_glsl_program_num > 1 ) { // multipass mode // GL_TEXTURE2/GL_TEXTURE3 pfn_glGenFramebuffers(2, (GLuint *)&texture->mpass_fbo_mamebm[0]); glGenTextures(2, (GLuint *)&texture->mpass_texture_mamebm[0]); for (i=0; i<2; i++) { if ( texture_fbo_create(texture->mpass_textureunit[i], texture->mpass_texture_mamebm[i], texture->mpass_fbo_mamebm[i], texture->rawwidth_create, texture->rawheight_create) ) { return -1; } } pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); osd_printf_verbose("GL texture: mpass mame-bmp 2x %dx%d (pow2 %dx%d)\n", texture->rawwidth, texture->rawheight, texture->rawwidth_create, texture->rawheight_create); } if( m_glsl_program_num > 1 && m_glsl_program_mb2sc < m_glsl_program_num - 1 ) { // multipass mode // GL_TEXTURE2/GL_TEXTURE3 pfn_glGenFramebuffers(2, (GLuint *)&texture->mpass_fbo_scrn[0]); glGenTextures(2, (GLuint *)&texture->mpass_texture_scrn[0]); for (i=0; i<2; i++) { if ( texture_fbo_create(texture->mpass_textureunit[i], texture->mpass_texture_scrn[i], texture->mpass_fbo_scrn[i], surf_w_pow2, surf_h_pow2) ) { return -1; } } osd_printf_verbose("GL texture: mpass screen-bmp 2x %dx%d (pow2 %dx%d)\n", m_width, m_height, surf_w_pow2, surf_h_pow2); } // GL_TEXTURE0 // get a name for this texture glGenTextures(1, (GLuint *)&texture->texture); pfn_glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texture->texture); glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->rawwidth_create); uint32_t * dummy = nullptr; GLint _width, _height; if ( gl_texture_check_size(GL_TEXTURE_2D, 0, GL_RGBA8, texture->rawwidth_create, texture->rawheight_create, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, &_width, &_height, 1) ) { osd_printf_error("cannot create bitmap texture, req: %dx%d, avail: %dx%d - bail out\n", texture->rawwidth_create, texture->rawheight_create, (int)_width, (int)_height); return -1; } dummy = (uint32_t *) malloc(texture->rawwidth_create * texture->rawheight_create * sizeof(uint32_t)); memset(dummy, 0, texture->rawwidth_create * texture->rawheight_create * sizeof(uint32_t)); glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, texture->rawwidth_create, texture->rawheight_create, 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, dummy); glFinish(); // should not be necessary, .. but make sure we won't access the memory after free free(dummy); if ((PRIMFLAG_GET_SCREENTEX(flags)) && video_config.filter) { assert( glsl_shader_feature == GLSL_SHADER_FEAT_PLAIN ); // screen textures get the user's choice of filtering glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { // non-screen textures will never be filtered glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } // set wrapping mode appropriately if (texture->flags & PRIMFLAG_TEXWRAP_MASK) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); } GL_CHECK_ERROR_NORMAL(); return 0; } ogl_texture_info *renderer_ogl::texture_create(const render_texinfo *texsource, uint32_t flags) { ogl_texture_info *texture; // allocate a new texture texture = global_alloc(ogl_texture_info); // fill in the core data texture->hash = texture_compute_hash(texsource, flags); texture->flags = flags; texture->texinfo = *texsource; texture->texinfo.seqid = -1; // force set data if (PRIMFLAG_GET_SCREENTEX(flags)) { auto win = assert_window(); texture->xprescale = win->prescale(); texture->yprescale = win->prescale(); } else { texture->xprescale = 1; texture->yprescale = 1; } // set the texture_format // // src/emu/validity.c:validate_display() states, // an emulated driver can only produce // BITMAP_FORMAT_IND16 and BITMAP_FORMAT_RGB32 // where only the first original paletted. // // other paletted formats, i.e.: // SDL_TEXFORMAT_RGB32_PALETTED, SDL_TEXFORMAT_RGB15_PALETTED and SDL_TEXFORMAT_YUY16_PALETTED // add features like brightness etc by the mame core // // all palette lookup may be implemented using shaders later on .. // that's why we keep the EQUAL flag TRUE, for all original true color bitmaps. // switch (PRIMFLAG_GET_TEXFORMAT(flags)) { case TEXFORMAT_ARGB32: texture->format = SDL_TEXFORMAT_ARGB32; break; case TEXFORMAT_RGB32: if (texsource->palette != nullptr) texture->format = SDL_TEXFORMAT_RGB32_PALETTED; else texture->format = SDL_TEXFORMAT_RGB32; break; case TEXFORMAT_PALETTE16: texture->format = SDL_TEXFORMAT_PALETTE16; break; case TEXFORMAT_YUY16: if (texsource->palette != nullptr) texture->format = SDL_TEXFORMAT_YUY16_PALETTED; else texture->format = SDL_TEXFORMAT_YUY16; break; default: osd_printf_error("Unknown textureformat %d\n", PRIMFLAG_GET_TEXFORMAT(flags)); } // compute the size texture_compute_size_type(texsource, texture, flags); texture->pbo=0; if ( texture->type != TEXTURE_TYPE_SHADER && m_useglsl) { pfn_glUseProgramObjectARB(0); // back to fixed function pipeline } if ( texture->type==TEXTURE_TYPE_SHADER ) { if ( texture_shader_create(texsource, texture, flags) ) { global_free(texture); return nullptr; } } else { // get a name for this texture glGenTextures(1, (GLuint *)&texture->texture); glEnable(texture->texTarget); // make sure we're operating on *this* texture glBindTexture(texture->texTarget, texture->texture); // this doesn't actually upload, it just sets up the PBO's parameters glTexImage2D(texture->texTarget, 0, GL_RGBA8, texture->rawwidth_create, texture->rawheight_create, texture->borderpix ? 1 : 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr); if ((PRIMFLAG_GET_SCREENTEX(flags)) && video_config.filter) { // screen textures get the user's choice of filtering glTexParameteri(texture->texTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(texture->texTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); } else { // non-screen textures will never be filtered glTexParameteri(texture->texTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); glTexParameteri(texture->texTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); } if( texture->texTarget==GL_TEXTURE_RECTANGLE_ARB ) { // texture rectangles can't wrap glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); } else { // set wrapping mode appropriately if (texture->flags & PRIMFLAG_TEXWRAP_MASK) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); } else { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); } } } if ( texture->type == TEXTURE_TYPE_DYNAMIC ) { assert(m_usepbo); // create the PBO pfn_glGenBuffers(1, (GLuint *)&texture->pbo); pfn_glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, texture->pbo); // set up the PBO dimension, .. pfn_glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, texture->rawwidth * texture->rawheight * sizeof(uint32_t), nullptr, GL_STREAM_DRAW); } if ( !texture->nocopy && texture->type!=TEXTURE_TYPE_DYNAMIC ) { texture->data = (uint32_t *) malloc(texture->rawwidth* texture->rawheight * sizeof(uint32_t)); texture->data_own=true; } // add us to the texture list if (m_texhash[texture->hash] == nullptr) m_texhash[texture->hash] = texture; else { int i; for (i = HASH_SIZE; i < HASH_SIZE + OVERFLOW_SIZE; i++) if (m_texhash[i] == nullptr) { m_texhash[i] = texture; break; } if ((HASH_SIZE + OVERFLOW_SIZE) <= i) throw emu_fatalerror("renderer_ogl::texture_create: texture hash exhausted ..."); } if (m_usevbo) { // Generate And Bind The Texture Coordinate Buffer pfn_glGenBuffers( 1, &(texture->texCoordBufferName) ); pfn_glBindBuffer( GL_ARRAY_BUFFER_ARB, texture->texCoordBufferName ); // Load The Data pfn_glBufferData( GL_ARRAY_BUFFER_ARB, 4*2*sizeof(GLfloat), texture->texCoord, GL_STREAM_DRAW ); glTexCoordPointer( 2, GL_FLOAT, 0, (char *) nullptr ); // we are using ARB VBO buffers } else { glTexCoordPointer(2, GL_FLOAT, 0, texture->texCoord); } return texture; } //============================================================ // copyline_palette16 //============================================================ static inline void copyline_palette16(uint32_t *dst, const uint16_t *src, int width, const rgb_t *palette, int xborderpix, int xprescale) { int x; assert(xborderpix == 0 || xborderpix == 1); if (xborderpix) *dst++ = 0xff000000 | palette[*src]; for (x = 0; x < width; x++) { int srcpix = *src++; uint32_t dstval = 0xff000000 | palette[srcpix]; for (int x2 = 0; x2 < xprescale; x2++) *dst++ = dstval; } if (xborderpix) *dst++ = 0xff000000 | palette[*--src]; } //============================================================ // copyline_rgb32 //============================================================ static inline void copyline_rgb32(uint32_t *dst, const uint32_t *src, int width, const rgb_t *palette, int xborderpix, int xprescale) { int x; assert(xborderpix == 0 || xborderpix == 1); // palette (really RGB map) case if (palette != nullptr) { if (xborderpix) { rgb_t srcpix = *src; *dst++ = 0xff000000 | palette[0x200 + srcpix.r()] | palette[0x100 + srcpix.g()] | palette[srcpix.b()]; } for (x = 0; x < width; x++) { rgb_t srcpix = *src++; uint32_t dstval = 0xff000000 | palette[0x200 + srcpix.r()] | palette[0x100 + srcpix.g()] | palette[srcpix.b()]; for (int x2 = 0; x2 < xprescale; x2++) *dst++ = dstval; } if (xborderpix) { rgb_t srcpix = *--src; *dst++ = 0xff000000 | palette[0x200 + srcpix.r()] | palette[0x100 + srcpix.g()] | palette[srcpix.b()]; } } // direct case else { if (xborderpix) *dst++ = 0xff000000 | *src; for (x = 0; x < width; x++) { rgb_t srcpix = *src++; uint32_t dstval = 0xff000000 | srcpix; for (int x2 = 0; x2 < xprescale; x2++) *dst++ = dstval; } if (xborderpix) *dst++ = 0xff000000 | *--src; } } //============================================================ // copyline_argb32 //============================================================ static inline void copyline_argb32(uint32_t *dst, const uint32_t *src, int width, const rgb_t *palette, int xborderpix, int xprescale) { int x; assert(xborderpix == 0 || xborderpix == 1); // palette (really RGB map) case if (palette != nullptr) { if (xborderpix) { rgb_t srcpix = *src; *dst++ = (srcpix & 0xff000000) | palette[0x200 + srcpix.r()] | palette[0x100 + srcpix.g()] | palette[srcpix.b()]; } for (x = 0; x < width; x++) { rgb_t srcpix = *src++; uint32_t dstval = (srcpix & 0xff000000) | palette[0x200 + srcpix.r()] | palette[0x100 + srcpix.g()] | palette[srcpix.b()]; for (int x2 = 0; x2 < xprescale; x2++) *dst++ = dstval; } if (xborderpix) { rgb_t srcpix = *--src; *dst++ = (srcpix & 0xff000000) | palette[0x200 + srcpix.r()] | palette[0x100 + srcpix.g()] | palette[srcpix.b()]; } } // direct case else { if (xborderpix) *dst++ = *src; for (x = 0; x < width; x++) { rgb_t srcpix = *src++; for (int x2 = 0; x2 < xprescale; x2++) *dst++ = srcpix; } if (xborderpix) *dst++ = *--src; } } static inline uint32_t ycc_to_rgb(uint8_t y, uint8_t cb, uint8_t cr) { /* original equations: C = Y - 16 D = Cb - 128 E = Cr - 128 R = clip(( 298 * C + 409 * E + 128) >> 8) G = clip(( 298 * C - 100 * D - 208 * E + 128) >> 8) B = clip(( 298 * C + 516 * D + 128) >> 8) R = clip(( 298 * (Y - 16) + 409 * (Cr - 128) + 128) >> 8) G = clip(( 298 * (Y - 16) - 100 * (Cb - 128) - 208 * (Cr - 128) + 128) >> 8) B = clip(( 298 * (Y - 16) + 516 * (Cb - 128) + 128) >> 8) R = clip(( 298 * Y - 298 * 16 + 409 * Cr - 409 * 128 + 128) >> 8) G = clip(( 298 * Y - 298 * 16 - 100 * Cb + 100 * 128 - 208 * Cr + 208 * 128 + 128) >> 8) B = clip(( 298 * Y - 298 * 16 + 516 * Cb - 516 * 128 + 128) >> 8) R = clip(( 298 * Y - 298 * 16 + 409 * Cr - 409 * 128 + 128) >> 8) G = clip(( 298 * Y - 298 * 16 - 100 * Cb + 100 * 128 - 208 * Cr + 208 * 128 + 128) >> 8) B = clip(( 298 * Y - 298 * 16 + 516 * Cb - 516 * 128 + 128) >> 8) */ int r, g, b, common; common = 298 * y - 298 * 16; r = (common + 409 * cr - 409 * 128 + 128) >> 8; g = (common - 100 * cb + 100 * 128 - 208 * cr + 208 * 128 + 128) >> 8; b = (common + 516 * cb - 516 * 128 + 128) >> 8; if (r < 0) r = 0; else if (r > 255) r = 255; if (g < 0) g = 0; else if (g > 255) g = 255; if (b < 0) b = 0; else if (b > 255) b = 255; return rgb_t(0xff, r, g, b); } //============================================================ // copyline_yuy16_to_argb //============================================================ static inline void copyline_yuy16_to_argb(uint32_t *dst, const uint16_t *src, int width, const rgb_t *palette, int xborderpix, int xprescale) { int x; assert(xborderpix == 0 || xborderpix == 2); assert(width % 2 == 0); // palette (really RGB map) case if (palette != nullptr) { if (xborderpix) { uint16_t srcpix0 = src[0]; uint16_t srcpix1 = src[1]; uint8_t cb = srcpix0 & 0xff; uint8_t cr = srcpix1 & 0xff; *dst++ = ycc_to_rgb(palette[0x000 + (srcpix0 >> 8)], cb, cr); *dst++ = ycc_to_rgb(palette[0x000 + (srcpix0 >> 8)], cb, cr); } for (x = 0; x < width / 2; x++) { uint16_t srcpix0 = *src++; uint16_t srcpix1 = *src++; uint8_t cb = srcpix0 & 0xff; uint8_t cr = srcpix1 & 0xff; uint32_t dstval0 = ycc_to_rgb(palette[0x000 + (srcpix0 >> 8)], cb, cr); uint32_t dstval1 = ycc_to_rgb(palette[0x000 + (srcpix1 >> 8)], cb, cr); for (int x2 = 0; x2 < xprescale; x2++) *dst++ = dstval0; for (int x2 = 0; x2 < xprescale; x2++) *dst++ = dstval1; } if (xborderpix) { uint16_t srcpix1 = *--src; uint16_t srcpix0 = *--src; uint8_t cb = srcpix0 & 0xff; uint8_t cr = srcpix1 & 0xff; *dst++ = ycc_to_rgb(palette[0x000 + (srcpix1 >> 8)], cb, cr); *dst++ = ycc_to_rgb(palette[0x000 + (srcpix1 >> 8)], cb, cr); } } // direct case else { if (xborderpix) { uint16_t srcpix0 = src[0]; uint16_t srcpix1 = src[1]; uint8_t cb = srcpix0 & 0xff; uint8_t cr = srcpix1 & 0xff; *dst++ = ycc_to_rgb(srcpix0 >> 8, cb, cr); *dst++ = ycc_to_rgb(srcpix0 >> 8, cb, cr); } for (x = 0; x < width; x += 2) { uint16_t srcpix0 = *src++; uint16_t srcpix1 = *src++; uint8_t cb = srcpix0 & 0xff; uint8_t cr = srcpix1 & 0xff; uint32_t dstval0 = ycc_to_rgb(srcpix0 >> 8, cb, cr); uint32_t dstval1 = ycc_to_rgb(srcpix1 >> 8, cb, cr); for (int x2 = 0; x2 < xprescale; x2++) *dst++ = dstval0; for (int x2 = 0; x2 < xprescale; x2++) *dst++ = dstval1; } if (xborderpix) { uint16_t srcpix1 = *--src; uint16_t srcpix0 = *--src; uint8_t cb = srcpix0 & 0xff; uint8_t cr = srcpix1 & 0xff; *dst++ = ycc_to_rgb(srcpix1 >> 8, cb, cr); *dst++ = ycc_to_rgb(srcpix1 >> 8, cb, cr); } } } //============================================================ // texture_set_data //============================================================ static void texture_set_data(ogl_texture_info *texture, const render_texinfo *texsource, uint32_t flags) { if ( texture->type == TEXTURE_TYPE_DYNAMIC ) { assert(texture->pbo); assert(!texture->nocopy); texture->data = (uint32_t *) pfn_glMapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY); } // note that nocopy and borderpix are mutually exclusive, IOW // they cannot be both true, thus this cannot lead to the // borderpix code below writing to texsource->base . if (texture->nocopy) { texture->data = (uint32_t *) texsource->base; } // always fill non-wrapping textures with an extra pixel on the top if (texture->borderpix) { memset(texture->data, 0, (texsource->width * texture->xprescale + 2) * sizeof(uint32_t)); } // when necessary copy (and convert) the data if (!texture->nocopy) { int y, y2; uint8_t *dst; for (y = 0; y < texsource->height; y++) { for (y2 = 0; y2 < texture->yprescale; y2++) { dst = (uint8_t *)(texture->data + (y * texture->yprescale + texture->borderpix + y2) * texture->rawwidth); switch (PRIMFLAG_GET_TEXFORMAT(flags)) { case TEXFORMAT_PALETTE16: copyline_palette16((uint32_t *)dst, (uint16_t *)texsource->base + y * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix, texture->xprescale); break; case TEXFORMAT_RGB32: copyline_rgb32((uint32_t *)dst, (uint32_t *)texsource->base + y * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix, texture->xprescale); break; case TEXFORMAT_ARGB32: copyline_argb32((uint32_t *)dst, (uint32_t *)texsource->base + y * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix, texture->xprescale); break; case TEXFORMAT_YUY16: copyline_yuy16_to_argb((uint32_t *)dst, (uint16_t *)texsource->base + y * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix, texture->xprescale); break; default: osd_printf_error("Unknown texture blendmode=%d format=%d\n", PRIMFLAG_GET_BLENDMODE(flags), PRIMFLAG_GET_TEXFORMAT(flags)); break; } } } } // always fill non-wrapping textures with an extra pixel on the bottom if (texture->borderpix) { memset((uint8_t *)texture->data + (texsource->height + 1) * texture->rawwidth * sizeof(uint32_t), 0, (texsource->width * texture->xprescale + 2) * sizeof(uint32_t)); } if ( texture->type == TEXTURE_TYPE_SHADER ) { pfn_glActiveTexture(GL_TEXTURE0); glBindTexture(texture->texTarget, texture->texture); if (texture->nocopy) glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->texinfo.rowpixels); else glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->rawwidth); // and upload the image glTexSubImage2D(texture->texTarget, 0, 0, 0, texture->rawwidth, texture->rawheight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, texture->data); } else if ( texture->type == TEXTURE_TYPE_DYNAMIC ) { glBindTexture(texture->texTarget, texture->texture); glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->rawwidth); // unmap the buffer from the CPU space so it can DMA pfn_glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB); // kick off the DMA glTexSubImage2D(texture->texTarget, 0, 0, 0, texture->rawwidth, texture->rawheight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, nullptr); } else { glBindTexture(texture->texTarget, texture->texture); // give the card a hint if (texture->nocopy) glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->texinfo.rowpixels); else glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->rawwidth); // and upload the image glTexSubImage2D(texture->texTarget, 0, 0, 0, texture->rawwidth, texture->rawheight, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, texture->data); } } //============================================================ // texture_find //============================================================ static int compare_texture_primitive(const ogl_texture_info *texture, const render_primitive *prim) { if (texture->texinfo.base == prim->texture.base && texture->texinfo.width == prim->texture.width && texture->texinfo.height == prim->texture.height && texture->texinfo.rowpixels == prim->texture.rowpixels && /* texture->texinfo.palette == prim->texture.palette && */ ((texture->flags ^ prim->flags) & (PRIMFLAG_BLENDMODE_MASK | PRIMFLAG_TEXFORMAT_MASK)) == 0) return 1; else return 0; } ogl_texture_info *renderer_ogl::texture_find(const render_primitive *prim) { HashT texhash = texture_compute_hash(&prim->texture, prim->flags); ogl_texture_info *texture; texture = m_texhash[texhash]; if (texture != nullptr) { int i; if (compare_texture_primitive(texture, prim)) return texture; for (i=HASH_SIZE; itype != TEXTURE_TYPE_SHADER || ( texture->type == TEXTURE_TYPE_SHADER && shaderIdx<=m_glsl_program_mb2sc ) ) { // compute the U/V scale factors if (texture->borderpix) { int unscaledwidth = (texture->rawwidth_create-2) / texture->xprescale + 2; int unscaledheight = (texture->rawheight_create-2) / texture->yprescale + 2; ustart = 1.0f / (float)(unscaledwidth); ustop = (float)(prim->texture.width + 1) / (float)(unscaledwidth); vstart = 1.0f / (float)(unscaledheight); vstop = (float)(prim->texture.height + 1) / (float)(unscaledheight); } else { ustop = (float)(prim->texture.width*texture->xprescale) / (float)texture->rawwidth_create; vstop = (float)(prim->texture.height*texture->yprescale) / (float)texture->rawheight_create; } } else if ( texture->type == TEXTURE_TYPE_SHADER && shaderIdx>m_glsl_program_mb2sc ) { int surf_w_pow2 = get_valid_pow2_value (m_width, texture->texpow2); int surf_h_pow2 = get_valid_pow2_value (m_height, texture->texpow2); ustop = (float)(m_width) / (float)surf_w_pow2; vstop = (float)(m_height) / (float)surf_h_pow2; } else { assert(0); // ?? } du = ustop - ustart; dv = vstop - vstart; if ( texture->texTarget == GL_TEXTURE_RECTANGLE_ARB ) { // texture coordinates for TEXTURE_RECTANGLE are 0,0 -> w,h // rather than 0,0 -> 1,1 as with normal OpenGL texturing du *= (float)texture->rawwidth; dv *= (float)texture->rawheight; } if ( texture->type == TEXTURE_TYPE_SHADER && shaderIdx!=m_glsl_program_mb2sc ) { // 1:1 tex coord CCW (0/0) (1/0) (1/1) (0/1) // we must go CW here due to the mame bitmap order texture->texCoord[0]=ustart + du * 0.0f; texture->texCoord[1]=vstart + dv * 1.0f; texture->texCoord[2]=ustart + du * 1.0f; texture->texCoord[3]=vstart + dv * 1.0f; texture->texCoord[4]=ustart + du * 1.0f; texture->texCoord[5]=vstart + dv * 0.0f; texture->texCoord[6]=ustart + du * 0.0f; texture->texCoord[7]=vstart + dv * 0.0f; } else { // transformation: mamebm -> scrn texture->texCoord[0]=ustart + du * prim->texcoords.tl.u; texture->texCoord[1]=vstart + dv * prim->texcoords.tl.v; texture->texCoord[2]=ustart + du * prim->texcoords.tr.u; texture->texCoord[3]=vstart + dv * prim->texcoords.tr.v; texture->texCoord[4]=ustart + du * prim->texcoords.br.u; texture->texCoord[5]=vstart + dv * prim->texcoords.br.v; texture->texCoord[6]=ustart + du * prim->texcoords.bl.u; texture->texCoord[7]=vstart + dv * prim->texcoords.bl.v; } } void renderer_ogl::texture_mpass_flip(ogl_texture_info *texture, int shaderIdx) { uint32_t mpass_src_idx = texture->mpass_dest_idx; texture->mpass_dest_idx = (mpass_src_idx+1) % 2; if ( shaderIdx>0 ) { int uniform_location; uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[shaderIdx], "mpass_texture"); pfn_glUniform1iARB(uniform_location, texture->mpass_textureunit[mpass_src_idx]-GL_TEXTURE0); GL_CHECK_ERROR_NORMAL(); } pfn_glActiveTexture(texture->mpass_textureunit[mpass_src_idx]); if ( shaderIdx<=m_glsl_program_mb2sc ) { glBindTexture(texture->texTarget, texture->mpass_texture_mamebm[mpass_src_idx]); } else { glBindTexture(texture->texTarget, texture->mpass_texture_scrn[mpass_src_idx]); } pfn_glActiveTexture(texture->mpass_textureunit[texture->mpass_dest_idx]); glBindTexture(texture->texTarget, 0); pfn_glActiveTexture(texture->mpass_textureunit[texture->mpass_dest_idx]); if ( shaderIdx=m_glsl_program_mb2sc ) { glBindTexture(texture->texTarget, texture->mpass_texture_scrn[texture->mpass_dest_idx]); pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, texture->mpass_fbo_scrn[texture->mpass_dest_idx]); } else { glBindTexture(texture->texTarget, texture->mpass_texture_mamebm[texture->mpass_dest_idx]); pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, texture->mpass_fbo_mamebm[texture->mpass_dest_idx]); } if ( shaderIdx==0 ) { glPushAttrib(GL_VIEWPORT_BIT); GL_CHECK_ERROR_NORMAL(); glViewport(0.0, 0.0, (GLsizei)texture->rawwidth, (GLsizei)texture->rawheight); } if ( shaderIdx==m_glsl_program_mb2sc ) { assert ( m_glsl_program_mb2sc < m_glsl_program_num-1 ); glPopAttrib(); // glViewport(0.0, 0.0, (GLsizei)window().width, (GLsizei)window().height) GL_CHECK_ERROR_NORMAL(); } glClear(GL_COLOR_BUFFER_BIT); // make sure the whole texture is redrawn .. } else { glBindTexture(texture->texTarget, 0); pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); if ( m_glsl_program_mb2sc == m_glsl_program_num-1 ) { glPopAttrib(); // glViewport(0.0, 0.0, (GLsizei)window().width, (GLsizei)window().height) GL_CHECK_ERROR_NORMAL(); } pfn_glActiveTexture(GL_TEXTURE0); glBindTexture(texture->texTarget, 0); } } void renderer_ogl::texture_shader_update(ogl_texture_info *texture, render_container *container, int shaderIdx) { int uniform_location; GLfloat vid_attributes[4]; if (container!=nullptr) { render_container::user_settings settings; container->get_user_settings(settings); /* FIXME: the code below is in just for illustration issue on * how to set shader variables. gamma, contrast and brightness are * handled already by the core */ vid_attributes[0] = settings.m_gamma; vid_attributes[1] = settings.m_contrast; vid_attributes[2] = settings.m_brightness; vid_attributes[3] = 0.0f; uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[shaderIdx], "vid_attributes"); pfn_glUniform4fvARB(uniform_location, 1, &(vid_attributes[shaderIdx])); if ( GL_CHECK_ERROR_QUIET() ) { osd_printf_verbose("GLSL: could not set 'vid_attributes' for shader prog idx %d\n", shaderIdx); } } } ogl_texture_info * renderer_ogl::texture_update(const render_primitive *prim, int shaderIdx) { ogl_texture_info *texture = texture_find(prim); int texBound = 0; // if we didn't find one, create a new texture if (texture == nullptr && prim->texture.base != nullptr) { texture = texture_create(&prim->texture, prim->flags); } else if (texture != nullptr) { if ( texture->type == TEXTURE_TYPE_SHADER ) { pfn_glUseProgramObjectARB(m_glsl_program[shaderIdx]); // back to our shader } else if ( texture->type == TEXTURE_TYPE_DYNAMIC ) { assert ( m_usepbo ) ; pfn_glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, texture->pbo); glEnable(texture->texTarget); } else { glEnable(texture->texTarget); } } if (texture != nullptr) { if ( texture->type == TEXTURE_TYPE_SHADER ) { texture_shader_update(texture, prim->container, shaderIdx); if ( m_glsl_program_num>1 ) { texture_mpass_flip(texture, shaderIdx); } } if ( shaderIdx==0 ) // redundant for subsequent multipass shader { if (prim->texture.base != nullptr && texture->texinfo.seqid != prim->texture.seqid) { texture->texinfo.seqid = prim->texture.seqid; // if we found it, but with a different seqid, copy the data texture_set_data(texture, &prim->texture, prim->flags); texBound=1; } } if (!texBound) { glBindTexture(texture->texTarget, texture->texture); } texture_coord_update(texture, prim, shaderIdx); glEnableClientState(GL_TEXTURE_COORD_ARRAY); if(m_usevbo) { pfn_glBindBuffer( GL_ARRAY_BUFFER_ARB, texture->texCoordBufferName ); // Load The Data pfn_glBufferSubData( GL_ARRAY_BUFFER_ARB, 0, 4*2*sizeof(GLfloat), texture->texCoord ); glTexCoordPointer( 2, GL_FLOAT, 0, (char *) nullptr ); // we are using ARB VBO buffers } else { glTexCoordPointer(2, GL_FLOAT, 0, texture->texCoord); } } return texture; } void renderer_ogl::texture_disable(ogl_texture_info * texture) { if ( texture->type == TEXTURE_TYPE_SHADER ) { assert ( m_useglsl ); pfn_glUseProgramObjectARB(0); // back to fixed function pipeline } else if ( texture->type == TEXTURE_TYPE_DYNAMIC ) { pfn_glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, 0); glDisable(texture->texTarget); } else { glDisable(texture->texTarget); } } void renderer_ogl::texture_all_disable() { if ( m_useglsl ) { pfn_glUseProgramObjectARB(0); // back to fixed function pipeline pfn_glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, 0); if ( m_usefbo ) pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); pfn_glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, 0); if ( m_usefbo ) pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); pfn_glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0); if ( m_usefbo ) pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); pfn_glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0); if ( m_usefbo ) pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); } glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); if(m_usetexturerect) { glDisable(GL_TEXTURE_RECTANGLE_ARB); } glDisable(GL_TEXTURE_2D); glDisableClientState(GL_TEXTURE_COORD_ARRAY); if(m_usevbo) { pfn_glBindBuffer( GL_ARRAY_BUFFER_ARB, 0); // unbind .. } if ( m_usepbo ) { pfn_glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, 0); } }