// 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 #include #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(); 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(); 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. 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)) #endif #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)