// 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. // //============================================================ #include "render_module.h" #include "modules/osdmodule.h" #if USE_OPENGL // will be picked up from specific OSD implementation #include "window.h" // OSD common headers #include "modules/lib/osdlib.h" #include "modules/lib/osdobj_common.h" #include "modules/opengl/gl_shader_mgr.h" #include "osdcomm.h" // OSD headers #if defined(OSD_WINDOWS) typedef uint64_t HashT; #include "winglcontext.h" #elif defined(OSD_MAC) #define GL_SILENCE_DEPRECATION (1) #include "osdmac.h" #else #include "osdsdl.h" #include "sdlglcontext.h" #endif // emu #include "emucore.h" #include "emuopts.h" #include "render.h" #if !defined(OSD_WINDOWS) && !defined(OSD_MAC) // standard SDL headers #define TOBEMIGRATED 1 #ifdef SDLMAME_SDL3 #include #else #include #endif #endif // !defined(OSD_WINDOWS && !defined(OSD_MAC) // standard C headers #include #include #include #include #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 // defined(SDLMAME_MACOSX) || defined(OSD_MAC) namespace osd { namespace { // 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 //============================================================ // Configuration //============================================================ #define GLSL_SHADER_MAX 10 struct ogl_video_config { ogl_video_config() = default; bool vbo = false; bool pbo = false; bool allowtexturerect = false; // allow GL_ARB_texture_rectangle, default: no bool forcepow2texture = false; // force power of two textures, default: no bool glsl = false; int glsl_filter = 0; // glsl filtering, >0 disables filter std::string glsl_shader_mamebm[GLSL_SHADER_MAX]; // custom glsl shader set, mame bitmap int glsl_shader_mamebm_num = 0; // custom glsl shader set number, mame bitmap std::string glsl_shader_scrn[GLSL_SHADER_MAX]; // custom glsl shader set, screen bitmap int glsl_shader_scrn_num = 0; // custom glsl shader number, screen bitmap }; //============================================================ // Textures //============================================================ /* ogl_texture_info holds information about a texture */ class ogl_texture_info { public: ogl_texture_info( #if defined(USE_DISPATCH_GL) osd_gl_dispatch *gld #endif ) #if defined(USE_DISPATCH_GL) : gl_dispatch(gld) #endif { for (int i=0; i<2; i++) { mpass_textureunit[i] = 0; mpass_texture_mamebm[i] = 0; mpass_fbo_mamebm[i] = 0; mpass_texture_scrn[i] = 0; mpass_fbo_scrn[i] = 0; } for (int i=0; i<8; i++) texCoord[i] = 0.0f; } #if defined(USE_DISPATCH_GL) osd_gl_dispatch *const gl_dispatch; // name is magic, can't be changed #endif HashT hash = 0; // hash value for the texture (must be >= pointer size) uint32_t flags = 0; // rendering flags render_texinfo texinfo; // copy of the texture info int rawwidth = 0, rawheight = 0; // raw width/height of the texture int rawwidth_create = 0; // raw width/height, pow2 compatible, if needed int rawheight_create = 0; // (create and initial set the texture, not for copy!) int type = 0; // what type of texture are we? int format = 0; // texture format int borderpix = 0; // do we have a 1 pixel border? int xprescale = 0; // what is our X prescale factor? int yprescale = 0; // what is our Y prescale factor? int nocopy = 0; // must the texture date be copied? uint32_t texture = 0; // OpenGL texture "name"/ID GLenum texTarget = 0; // OpenGL texture target int texpow2 = 0; // Is this texture pow2 uint32_t mpass_dest_idx = 0; // Multipass dest idx [0..1] uint32_t mpass_textureunit[2]; // texture unit names for GLSL uint32_t mpass_texture_mamebm[2]; // Multipass OpenGL texture "name"/ID for the shader uint32_t mpass_fbo_mamebm[2]; // framebuffer object for this texture, multipass uint32_t mpass_texture_scrn[2]; // Multipass OpenGL texture "name"/ID for the shader uint32_t mpass_fbo_scrn[2]; // framebuffer object for this texture, multipass uint32_t pbo = 0; // pixel buffer object for this texture (DYNAMIC only!) uint32_t *data = nullptr; // pixels for the texture bool data_own = false; // do we own / allocated it ? GLfloat texCoord[8]; GLuint texCoordBufferName; }; // renderer_ogl is the information about OpenGL for the current screen class renderer_ogl : public osd_renderer { public: renderer_ogl(osd_window &window, ogl_video_config const &ogl_config) : osd_renderer(window) , m_ogl_config(ogl_config) , m_blittimer(0) , m_width(0) , m_height(0) , m_blit_dim(0, 0) , m_initialized(0) , m_last_blendmode(0) , m_texture_max_width(0) , m_texture_max_height(0) , m_texpoweroftwo(0) , m_usevbo(0) , m_usepbo(0) , m_usefbo(0) , m_useglsl(0) , m_glsl_program_num(0) , m_glsl_program_mb2sc(0) , m_usetexturerect(0) , m_init_context(0) , m_last_hofs(0.0f) , m_last_vofs(0.0f) , m_surf_w(0) , m_surf_h(0) { for (int i=0; i < HASH_SIZE + OVERFLOW_SIZE; i++) m_texhash[i] = nullptr; for (int i=0; i < 2*GLSL_SHADER_MAX; i++) m_glsl_program[i] = 0; for (int i=0; i < 8; i++) m_texVerticex[i] = 0.0f; } virtual ~renderer_ogl() { // free the memory in the window destroy_all_textures(); } virtual int create() override; virtual int draw(const int update) override; #ifndef OSD_WINDOWS virtual int xy_to_render_target(const int x, const int y, int *xt, int *yt) override; #endif virtual render_primitive_list *get_primitives() override { osd_dim nd = window().get_size_pixels(); if (nd != m_blit_dim) { m_blit_dim = nd; notify_changed(); } if ((m_blit_dim.width() == 0) || (m_blit_dim.height() == 0)) return nullptr; window().target()->set_bounds(m_blit_dim.width(), m_blit_dim.height(), window().pixel_aspect()); return &window().target()->get_primitives(); } #ifdef OSD_WINDOWS virtual void save() override { } virtual void record() override { } virtual void toggle_fsfx() override { } #endif private: static const uint32_t HASH_SIZE = ((1 << 18) + 1); static const uint32_t OVERFLOW_SIZE = (1 << 12); void destroy_all_textures(); void loadGLExtensions(); void loadgl_functions(); void initialize_gl(); void set_blendmode(int blendmode); HashT texture_compute_hash(const render_texinfo *texture, uint32_t flags); void texture_compute_type_subroutine(const render_texinfo *texsource, ogl_texture_info *texture, uint32_t flags); void 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); void texture_compute_size_type(const render_texinfo *texsource, ogl_texture_info *texture, uint32_t flags); ogl_texture_info *texture_create(const render_texinfo *texsource, uint32_t flags); int texture_shader_create(const render_texinfo *texsource, ogl_texture_info *texture, uint32_t flags); ogl_texture_info *texture_find(const render_primitive *prim); void texture_coord_update(ogl_texture_info *texture, const render_primitive *prim, int shaderIdx); void texture_mpass_flip(ogl_texture_info *texture, int shaderIdx); void texture_shader_update(ogl_texture_info *texture, render_container *container, int shaderIdx); ogl_texture_info * texture_update(const render_primitive *prim, int shaderIdx); void texture_disable(ogl_texture_info * texture); void texture_all_disable(); int gl_checkFramebufferStatus() const; int texture_fbo_create(uint32_t text_unit, uint32_t text_name, uint32_t fbo_name, int width, int height) const; void texture_set_data(ogl_texture_info *texture, const render_texinfo *texsource, uint32_t flags) const; int gl_check_error(bool log, const char *file, int line) const { GLenum const glerr = glGetError(); if (GL_NO_ERROR != glerr) { if (log) osd_printf_warning("%s:%d: GL Error: %d 0x%X\n", file, line, int(glerr), unsigned(glerr)); } return (GL_NO_ERROR != glerr) ? glerr : 0; } #define GL_CHECK_ERROR_QUIET() gl_check_error(false, __FILE__, __LINE__) #define GL_CHECK_ERROR_NORMAL() gl_check_error(true, __FILE__, __LINE__) ogl_video_config const &m_ogl_config; int32_t m_blittimer; int m_width; int m_height; osd_dim m_blit_dim; std::unique_ptr m_gl_context; std::unique_ptr m_shader_tool; int m_initialized; // is everything well initialized, i.e. all GL stuff etc. // 3D info (GL mode only) ogl_texture_info * m_texhash[HASH_SIZE + OVERFLOW_SIZE]; int m_last_blendmode; // previous blendmode int32_t m_texture_max_width; // texture maximum width int32_t m_texture_max_height; // texture maximum height int m_texpoweroftwo; // must textures be power-of-2 sized? int m_usevbo; // runtime check if VBO is available int m_usepbo; // runtime check if PBO is available int m_usefbo; // runtime check if FBO is available int m_useglsl; // runtime check if GLSL is available GLhandleARB m_glsl_program[2*GLSL_SHADER_MAX]; // GLSL programs, or 0 int m_glsl_program_num; // number of GLSL programs int m_glsl_program_mb2sc; // GLSL program idx, which transforms // the mame-bitmap. screen-bitmap (size/rotation/..) // All progs <= glsl_program_mb2sc using the mame bitmap // as input, otherwise the screen bitmap. // All progs >= glsl_program_mb2sc using the screen bitmap // as output, otherwise the mame bitmap. int m_usetexturerect; // use ARB_texture_rectangle for non-power-of-2, general use int m_init_context; // initialize context before next draw float m_last_hofs; float m_last_vofs; // Static vars from draogl_window_dra int32_t m_surf_w; int32_t m_surf_h; GLfloat m_texVerticex[8]; #if defined(USE_DISPATCH_GL) std::unique_ptr gl_dispatch; // name is magic, can't be changed #endif // OGL 1.3 #if defined(GL_ARB_multitexture) && !defined(OSD_MAC) PFNGLACTIVETEXTUREARBPROC m_glActiveTexture = nullptr; #else PFNGLACTIVETEXTUREPROC m_glActiveTexture = nullptr; #endif // VBO PFNGLGENBUFFERSPROC m_glGenBuffers = nullptr; PFNGLDELETEBUFFERSPROC m_glDeleteBuffers = nullptr; PFNGLBINDBUFFERPROC m_glBindBuffer = nullptr; PFNGLBUFFERDATAPROC m_glBufferData = nullptr; PFNGLBUFFERSUBDATAPROC m_glBufferSubData = nullptr; // PBO PFNGLMAPBUFFERPROC m_glMapBuffer = nullptr; PFNGLUNMAPBUFFERPROC m_glUnmapBuffer = nullptr; // FBO PFNGLISFRAMEBUFFEREXTPROC m_glIsFramebuffer = nullptr; PFNGLBINDFRAMEBUFFEREXTPROC m_glBindFramebuffer = nullptr; PFNGLDELETEFRAMEBUFFERSEXTPROC m_glDeleteFramebuffers = nullptr; PFNGLGENFRAMEBUFFERSEXTPROC m_glGenFramebuffers = nullptr; PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC m_glCheckFramebufferStatus = nullptr; PFNGLFRAMEBUFFERTEXTURE2DEXTPROC m_glFramebufferTexture2D = nullptr; static bool s_shown_video_info; }; //============================================================ // 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 int glsl_shader_feature = glsl_shader_info::FEAT_PLAIN; // FIXME: why is this static? //============================================================ // Static Variables //============================================================ bool renderer_ogl::s_shown_video_info = false; //============================================================ // Load the OGL function addresses //============================================================ void renderer_ogl::loadgl_functions() { #if defined(USE_DISPATCH_GL) gl_dispatch = std::make_unique(); 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 (!m_gl_context->get_proc_address(func, #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 } 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 (m_ogl_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 ( !m_ogl_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 = m_ogl_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 = m_ogl_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 = m_ogl_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() { // create renderer #if defined(OSD_WINDOWS) m_gl_context.reset(new win_gl_context(dynamic_cast(window()).platform_window())); #elif defined(OSD_MAC) // TODO // m_gl_context.reset(new mac_gl_context(dynamic_cast(window()).platform_window())); #else m_gl_context.reset(new sdl_gl_context(dynamic_cast(window()).platform_window())); #endif if (!*m_gl_context) { char const *const msg = m_gl_context->last_error_message(); osd_printf_error("Creating OpenGL context failed: %s\n", msg ? msg : "unknown error"); return 1; } m_gl_context->set_swap_interval(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(); 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() { if (!m_initialized) return; m_gl_context->make_current(); bool const lock = bool(window().m_primlist); if (lock) window().m_primlist->acquire_lock(); glFinish(); texture_all_disable(); glFinish(); glDisableClientState(GL_VERTEX_ARRAY); for (int i = 0; i < (HASH_SIZE + OVERFLOW_SIZE); ++i) { ogl_texture_info *const texture = std::exchange(m_texhash[i], nullptr); if (texture) { if (m_usevbo) { m_glDeleteBuffers( 1, &(texture->texCoordBufferName) ); texture->texCoordBufferName=0; } if (m_usepbo && texture->pbo) { m_glDeleteBuffers( 1, (GLuint *)&(texture->pbo) ); texture->pbo=0; } if (m_glsl_program_num > 1) { assert(m_usefbo); m_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); m_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; } delete texture; } } m_shader_tool.reset(); m_initialized = 0; if (lock) window().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) { m_gl_context->get_proc_address(m_glGenBuffers, "glGenBuffers"); m_gl_context->get_proc_address(m_glDeleteBuffers, "glDeleteBuffers"); m_gl_context->get_proc_address(m_glBindBuffer, "glBindBuffer"); m_gl_context->get_proc_address(m_glBufferData, "glBufferData"); m_gl_context->get_proc_address(m_glBufferSubData, "glBufferSubData"); } // PBO: if (m_usepbo) { m_gl_context->get_proc_address(m_glMapBuffer, "glMapBuffer"); m_gl_context->get_proc_address(m_glUnmapBuffer, "glUnmapBuffer"); } // FBO: if (m_usefbo) { m_gl_context->get_proc_address(m_glIsFramebuffer, "glIsFramebufferEXT"); m_gl_context->get_proc_address(m_glBindFramebuffer, "glBindFramebufferEXT"); m_gl_context->get_proc_address(m_glDeleteFramebuffers, "glDeleteFramebuffersEXT"); m_gl_context->get_proc_address(m_glGenFramebuffers, "glGenFramebuffersEXT"); m_gl_context->get_proc_address(m_glCheckFramebufferStatus, "glCheckFramebufferStatusEXT"); m_gl_context->get_proc_address(m_glFramebufferTexture2D, "glFramebufferTexture2DEXT"); } if (m_usevbo && (!m_glGenBuffers || !m_glDeleteBuffers || !m_glBindBuffer || !m_glBufferData || !m_glBufferSubData)) { m_usepbo = false; if (_once) { osd_printf_warning("OpenGL: VBO not supported, missing:"); if (!m_glGenBuffers) osd_printf_warning(" glGenBuffers"); if (!m_glDeleteBuffers) osd_printf_warning(" glDeleteBuffers"); if (!m_glBindBuffer) osd_printf_warning(" glBindBuffer"); if (!m_glBufferData) osd_printf_warning(" glBufferData"); if (!m_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 && (!m_glMapBuffer || !m_glUnmapBuffer)) { m_usepbo = false; if (_once) { osd_printf_warning("OpenGL: PBO not supported, missing:"); if (!m_glMapBuffer) osd_printf_warning(" glMapBuffer"); if (!m_glUnmapBuffer) osd_printf_warning(" glUnmapBuffer"); osd_printf_warning("\n"); } } if ( m_usefbo && ( !m_glIsFramebuffer || !m_glBindFramebuffer || !m_glDeleteFramebuffers || !m_glGenFramebuffers || !m_glCheckFramebufferStatus || !m_glFramebufferTexture2D )) { m_usefbo = false; if (_once) { osd_printf_warning("OpenGL: FBO not supported, missing:"); if (!m_glIsFramebuffer) osd_printf_warning(" m_glIsFramebuffer"); if (!m_glBindFramebuffer) osd_printf_warning(" m_glBindFramebuffer"); if (!m_glDeleteFramebuffers) osd_printf_warning(" m_glDeleteFramebuffers"); if (!m_glGenFramebuffers) osd_printf_warning(" m_glGenFramebuffers"); if (!m_glCheckFramebufferStatus) osd_printf_warning(" m_glCheckFramebufferStatus"); if (!m_glFramebufferTexture2D) osd_printf_warning(" m_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) m_gl_context->get_proc_address(m_glActiveTexture, "glActiveTextureARB"); #else m_gl_context->get_proc_address(m_glActiveTexture, "glActiveTexture"); #endif if (!m_glActiveTexture) { if (_once) osd_printf_warning("OpenGL: GLSL disabled, glActiveTexture(ARB) not supported\n"); m_useglsl = 0; } } if (m_useglsl) { m_shader_tool = glsl_shader_info::init( *m_gl_context #if defined(USE_DISPATCH_GL) , gl_dispatch.get() #endif ); m_useglsl = (m_shader_tool ? 1 : 0); if ( ! m_useglsl ) { if (_once) { osd_printf_warning("OpenGL: GLSL supported, but shader instantiation failed - disabled\n"); } } } if (m_useglsl) { if (window().prescale() != 1 ) { m_useglsl = 0; if (_once) { osd_printf_warning("OpenGL: GLSL supported, but disabled due to: prescale !=1 \n"); } } } if (m_useglsl) { video_config.filter = false; glsl_shader_feature = glsl_shader_info::FEAT_PLAIN; m_glsl_program_num = 0; m_glsl_program_mb2sc = 0; for (int i=0; iadd_mamebm(m_ogl_config.glsl_shader_mamebm[i].c_str(), m_glsl_program_num) ) { osd_printf_error("OpenGL: GLSL loading mame bitmap shader %d failed (%s)\n", i, m_ogl_config.glsl_shader_mamebm[i]); } else { glsl_shader_feature = glsl_shader_info::FEAT_CUSTOM; if (_once) { osd_printf_verbose("OpenGL: GLSL using mame bitmap shader filter %d: '%s'\n", m_glsl_program_num, m_ogl_config.glsl_shader_mamebm[i]); } m_glsl_program_mb2sc = m_glsl_program_num; // the last mame_bitmap (mb) shader does it. m_glsl_program_num++; } } if ( m_ogl_config.glsl_shader_scrn_num > 0 && m_glsl_program_num==0 ) { osd_printf_verbose("OpenGL: GLSL cannot use screen bitmap shader without bitmap shader\n"); } for(int i=0; m_usefbo && m_glsl_program_num>0 && iadd_scrn(m_ogl_config.glsl_shader_scrn[i].c_str(), m_glsl_program_num-1-m_glsl_program_mb2sc) ) { osd_printf_error("OpenGL: GLSL loading screen bitmap shader %d failed (%s)\n", i, m_ogl_config.glsl_shader_scrn[i]); } else { if (_once) { osd_printf_verbose("OpenGL: GLSL using screen bitmap shader filter %d: '%s'\n", m_glsl_program_num, m_ogl_config.glsl_shader_scrn[i]); } m_glsl_program_num++; } } if ( 0==m_glsl_program_num && 0 <= m_ogl_config.glsl_filter && m_ogl_config.glsl_filter < glsl_shader_info::FEAT_INT_NUMBER ) { m_glsl_program_mb2sc = m_glsl_program_num; // the last mame_bitmap (mb) shader does it. m_glsl_program_num++; glsl_shader_feature = m_ogl_config.glsl_filter; if (_once) { osd_printf_verbose("OpenGL: GLSL using shader filter '%s', idx: %d, num %d (vid filter: %d)\n", m_shader_tool->get_filter_name_mamebm(glsl_shader_feature), glsl_shader_feature, m_glsl_program_num, video_config.filter); } } } else { if (_once) { osd_printf_verbose("OpenGL: using vid filter: %d\n", video_config.filter); } } _once = 0; } //============================================================ // sdl_info::draw //============================================================ int renderer_ogl::draw(const int update) { ogl_texture_info *texture=nullptr; float vofs, hofs; int pendingPrimitive=GL_NO_PRIMITIVE, curPrimitive=GL_NO_PRIMITIVE; osd_dim wdim = window().get_size_pixels(); 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->make_current(); 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. */ #if !defined(SDLMAME_MACOSX) && defined(OSD_MAC) { // 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; } } #endif m_last_hofs = hofs; m_last_vofs = vofs; window().m_primlist->acquire_lock(); // now draw for (render_primitive &prim : *window().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) { if (curPrimitive==GL_POINTS) glPointSize(prim.width); else glLineWidth(prim.width); 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 { // 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 float effwidth = std::max(prim.width(), 0.5f); // determine the bounds of a quad to draw this line auto [b0, b1] = render_line_to_quad(prim.bounds, effwidth, 0.0f); // 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 (const line_aa_step *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 float r = std::min(prim.color.r * step->weight, 1.0f); float g = std::min(prim.color.g * step->weight, 1.0f); float b = std::min(prim.color.b * step->weight, 1.0f); float a = std::min(prim.color.a * 255.0f, 1.0f); 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; } window().m_primlist->release_lock(); m_init_context = 0; m_gl_context->swap_buffer(); 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--; if (PRIMFLAG_GET_SCREENTEX(flags) && (texture->xprescale != window().prescale() || texture->yprescale != window().prescale())) osd_printf_warning("SDL: adjusting prescale from %dx%d to %dx%d\n", window().prescale(), window().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 //============================================================ int renderer_ogl::gl_checkFramebufferStatus() const { GLenum const status = (GLenum)m_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; } int renderer_ogl::texture_fbo_create(uint32_t text_unit, uint32_t text_name, uint32_t fbo_name, int width, int height) const { m_glActiveTexture(text_unit); m_glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo_name); glBindTexture(GL_TEXTURE_2D, text_name); { GLint _width, _height; if ( m_shader_tool->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_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); m_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; iget_program_mamebm(glsl_shader_feature, i); else m_glsl_program[i] = m_shader_tool->get_program_scrn(i-1-m_glsl_program_mb2sc); m_shader_tool->pfn_glUseProgramObjectARB(m_glsl_program[i]); if ( i<=m_glsl_program_mb2sc ) { // GL_TEXTURE0 GLSL Uniforms uniform_location = m_shader_tool->pfn_glGetUniformLocationARB(m_glsl_program[i], "color_texture"); m_shader_tool->pfn_glUniform1iARB(uniform_location, 0); GL_CHECK_ERROR_NORMAL(); } { GLfloat color_texture_sz[2] = { (GLfloat)texture->rawwidth, (GLfloat)texture->rawheight }; uniform_location = m_shader_tool->pfn_glGetUniformLocationARB(m_glsl_program[i], "color_texture_sz"); m_shader_tool->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 = m_shader_tool->pfn_glGetUniformLocationARB(m_glsl_program[i], "color_texture_pow2_sz"); m_shader_tool->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 = m_shader_tool->pfn_glGetUniformLocationARB(m_glsl_program[i], "screen_texture_sz"); m_shader_tool->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 = m_shader_tool->pfn_glGetUniformLocationARB(m_glsl_program[i], "screen_texture_pow2_sz"); m_shader_tool->pfn_glUniform2fvARB(uniform_location, 1, &(screen_texture_pow2_sz[0])); GL_CHECK_ERROR_NORMAL(); } m_shader_tool->pfn_glUseProgramObjectARB(m_glsl_program[0]); // start with 1st shader if( m_glsl_program_num > 1 ) { // multipass mode // GL_TEXTURE2/GL_TEXTURE3 m_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; } } m_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 m_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); m_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 ( m_shader_tool->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_info::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_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } 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 = new ogl_texture_info( #if defined(USE_DISPATCH_GL) gl_dispatch.get() #endif ); // 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)) { texture->xprescale = window().prescale(); texture->yprescale = window().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) { m_shader_tool->pfn_glUseProgramObjectARB(0); // back to fixed function pipeline } if ( texture->type==TEXTURE_TYPE_SHADER ) { if ( texture_shader_create(texsource, texture, flags) ) { delete 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_EDGE); glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } 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_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); } } } if ( texture->type == TEXTURE_TYPE_DYNAMIC ) { assert(m_usepbo); // create the PBO m_glGenBuffers(1, (GLuint *)&texture->pbo); m_glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, texture->pbo); // set up the PBO dimension, .. m_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 m_glGenBuffers( 1, &(texture->texCoordBufferName) ); m_glBindBuffer( GL_ARRAY_BUFFER_ARB, texture->texCoordBufferName ); // Load The Data m_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 //============================================================ void renderer_ogl::texture_set_data(ogl_texture_info *texture, const render_texinfo *texsource, uint32_t flags) const { if ( texture->type == TEXTURE_TYPE_DYNAMIC ) { assert(texture->pbo); assert(!texture->nocopy); texture->data = (uint32_t *) m_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 ) { m_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 m_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 = m_shader_tool->pfn_glGetUniformLocationARB(m_glsl_program[shaderIdx], "mpass_texture"); m_shader_tool->pfn_glUniform1iARB(uniform_location, texture->mpass_textureunit[mpass_src_idx]-GL_TEXTURE0); GL_CHECK_ERROR_NORMAL(); } m_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]); } m_glActiveTexture(texture->mpass_textureunit[texture->mpass_dest_idx]); glBindTexture(texture->texTarget, 0); m_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]); m_glBindFramebuffer(GL_FRAMEBUFFER_EXT, texture->mpass_fbo_scrn[texture->mpass_dest_idx]); } else { glBindTexture(texture->texTarget, texture->mpass_texture_mamebm[texture->mpass_dest_idx]); m_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); m_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(); } m_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(); /* 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 = m_shader_tool->pfn_glGetUniformLocationARB(m_glsl_program[shaderIdx], "vid_attributes"); m_shader_tool->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 ) { m_shader_tool->pfn_glUseProgramObjectARB(m_glsl_program[shaderIdx]); // back to our shader } else if ( texture->type == TEXTURE_TYPE_DYNAMIC ) { assert ( m_usepbo ) ; m_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) { m_glBindBuffer( GL_ARRAY_BUFFER_ARB, texture->texCoordBufferName ); // Load The Data m_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 ); m_shader_tool->pfn_glUseProgramObjectARB(0); // back to fixed function pipeline } else if ( texture->type == TEXTURE_TYPE_DYNAMIC ) { m_glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, 0); glDisable(texture->texTarget); } else { glDisable(texture->texTarget); } } void renderer_ogl::texture_all_disable() { if (m_useglsl) { m_shader_tool->pfn_glUseProgramObjectARB(0); // back to fixed function pipeline m_glActiveTexture(GL_TEXTURE3); glBindTexture(GL_TEXTURE_2D, 0); if (m_usefbo) m_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); m_glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, 0); if (m_usefbo) m_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); m_glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, 0); if (m_usefbo) m_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); m_glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, 0); if (m_usefbo) m_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) { m_glBindBuffer( GL_ARRAY_BUFFER_ARB, 0); // unbind .. } if ( m_usepbo ) { m_glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, 0); } } class video_opengl : public osd_module, public render_module { public: video_opengl() : osd_module(OSD_RENDERER_PROVIDER, "opengl") #if defined(USE_DISPATCH_GL) , m_dll_loaded(false) #endif { } ~video_opengl() { exit(); } virtual int init(osd_interface &osd, osd_options const &options) override; virtual void exit() override; virtual std::unique_ptr create(osd_window &window) override; protected: virtual unsigned flags() const override { return FLAG_INTERACTIVE | FLAG_SDL_NEEDS_OPENGL; } private: ogl_video_config m_ogl_config; #if defined(USE_DISPATCH_GL) bool m_dll_loaded; #endif }; int video_opengl::init(osd_interface &osd, osd_options const &options) { m_ogl_config.vbo = options.gl_vbo(); m_ogl_config.pbo = options.gl_pbo(); m_ogl_config.allowtexturerect = !options.gl_no_texture_rect(); m_ogl_config.forcepow2texture = options.gl_force_pow2_texture(); m_ogl_config.glsl = options.gl_glsl(); if (m_ogl_config.glsl) { m_ogl_config.glsl_filter = options.glsl_filter(); m_ogl_config.glsl_shader_mamebm_num = 0; for (int i = 0; i < GLSL_SHADER_MAX; i++) { char const *const stemp = options.shader_mame(i); if (stemp && *stemp && strcmp(stemp, OSDOPTVAL_NONE)) m_ogl_config.glsl_shader_mamebm[m_ogl_config.glsl_shader_mamebm_num++] = stemp; } m_ogl_config.glsl_shader_scrn_num = 0; for (int i = 0; i < GLSL_SHADER_MAX; i++) { char const *const stemp = options.shader_screen(i); if (stemp && *stemp && strcmp(stemp, OSDOPTVAL_NONE)) m_ogl_config.glsl_shader_scrn[m_ogl_config.glsl_shader_scrn_num++] = stemp; } } else { m_ogl_config.glsl_filter = 0; m_ogl_config.glsl_shader_mamebm_num = 0; for (std::string &s : m_ogl_config.glsl_shader_mamebm) s.clear(); m_ogl_config.glsl_shader_scrn_num = 0; for (std::string &s : m_ogl_config.glsl_shader_scrn) s.clear(); } #if defined(USE_DISPATCH_GL) #if defined(OSD_SDL) if (!m_dll_loaded) { // directfb and and x11 use this env var: SDL_VIDEO_GL_DRIVER char const *libname = dynamic_cast(options).gl_lib(); if (libname && (!*libname || !std::strcmp(libname, OSDOPTVAL_AUTO))) libname = nullptr; if (SDL_GL_LoadLibrary(libname) != 0) { osd_printf_error("Unable to load OpenGL shared library: %s\n", libname ? libname : ""); return -1; } osd_printf_verbose("Loaded OpenGL shared library: %s\n", libname ? libname : ""); } #endif // defined(OSD_SDL) m_dll_loaded = true; #endif // defined(USE_DISPATCH_GL) #if defined(OSD_WINDOWS) osd_printf_verbose("Using Windows OpenGL driver\n"); #else // defined(OSD_WINDOWS) osd_printf_verbose("Using SDL multi-window OpenGL driver (SDL 2.0+)\n"); #endif // defined(OSD_WINDOWS) return 0; } void video_opengl::exit() { #if defined(USE_DISPATCH_GL) #if defined(OSD_SDL) if (m_dll_loaded) SDL_GL_UnloadLibrary(); #endif // defined(OSD_SDL) m_dll_loaded = false; #endif // defined(USE_DISPATCH_GL) m_ogl_config = ogl_video_config(); } std::unique_ptr video_opengl::create(osd_window &window) { return std::make_unique(window, m_ogl_config); } } // anonymous namespace } // namespace osd #else // USE_OPENGL namespace osd { namespace { MODULE_NOT_SUPPORTED(video_opengl, OSD_RENDERER_PROVIDER, "opengl") } } #endif // USE_OPENGL MODULE_DEFINITION(RENDERER_OPENGL, osd::video_opengl)