diff options
Diffstat (limited to 'src/osd/modules/render/drawogl.cpp')
-rw-r--r-- | src/osd/modules/render/drawogl.cpp | 3294 |
1 files changed, 3294 insertions, 0 deletions
diff --git a/src/osd/modules/render/drawogl.cpp b/src/osd/modules/render/drawogl.cpp new file mode 100644 index 00000000000..5a139c4054b --- /dev/null +++ b/src/osd/modules/render/drawogl.cpp @@ -0,0 +1,3294 @@ +// license:BSD-3-Clause +// copyright-holders:Olivier Galibert, R. Belmont +//============================================================ +// +// drawogl.c - SDL software and OpenGL implementation +// +// SDLMAME by Olivier Galibert and R. Belmont +// +// Note: D3D9 goes to a lot of trouble to fiddle with MODULATE +// mode on textures. That is the default in OpenGL so we +// don't have to touch it. +// +//============================================================ + +// standard C headers +#include <math.h> +#include <stdio.h> + +// MAME headers +#include "osdcomm.h" +#include "emu.h" +#include "options.h" +#include "emuopts.h" + +#ifndef OSD_WINDOWS +// standard SDL headers +#define TOBEMIGRATED 1 +#include "sdlinc.h" +#endif + +#include "modules/lib/osdlib.h" +#include "modules/lib/osdobj_common.h" + +#if defined(OSD_WINDOWS) && !defined(SDLMAME_SDL2) +#define SDLMAME_SDL2 0 +#endif + +// OpenGL headers +#include "modules/opengl/osd_opengl.h" + +#include "modules/opengl/gl_shader_tool.h" +#include "modules/opengl/gl_shader_mgr.h" + +#if defined(SDLMAME_MACOSX) +#ifndef APIENTRY +#define APIENTRY +#endif +#ifndef APIENTRYP +#define APIENTRYP APIENTRY * +#endif + +typedef void (APIENTRYP PFNGLGENBUFFERSPROC) (GLsizei n, GLuint *buffers); +typedef void (APIENTRYP PFNGLBINDBUFFERPROC) (GLenum, GLuint); +typedef void (APIENTRYP PFNGLBUFFERDATAPROC) (GLenum, GLsizeiptr, const GLvoid *, GLenum); +typedef void (APIENTRYP PFNGLBUFFERSUBDATAPROC) (GLenum, GLintptr, GLsizeiptr, const GLvoid *); +typedef GLvoid* (APIENTRYP PFNGLMAPBUFFERPROC) (GLenum, GLenum); +typedef GLboolean (APIENTRYP PFNGLUNMAPBUFFERPROC) (GLenum); +typedef void (APIENTRYP PFNGLDELETEBUFFERSPROC) (GLsizei, const GLuint *); +typedef void (APIENTRYP PFNGLACTIVETEXTUREPROC) (GLenum texture); +typedef GLboolean (APIENTRYP PFNGLISFRAMEBUFFEREXTPROC) (GLuint framebuffer); +typedef void (APIENTRYP PFNGLBINDFRAMEBUFFEREXTPROC) (GLenum m_target, GLuint framebuffer); +typedef void (APIENTRYP PFNGLDELETEFRAMEBUFFERSEXTPROC) (GLsizei n, const GLuint *framebuffers); +typedef void (APIENTRYP PFNGLGENFRAMEBUFFERSEXTPROC) (GLsizei n, GLuint *framebuffers); +typedef GLenum (APIENTRYP PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) (GLenum m_target); +typedef void (APIENTRYP PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) (GLenum m_target, GLenum attachment, GLenum textarget, GLuint texture, GLint level); +typedef void (APIENTRYP PFNGLGENRENDERBUFFERSEXTPROC) (GLsizei n, GLuint *renderbuffers); +typedef void (APIENTRYP PFNGLBINDRENDERBUFFEREXTPROC) (GLenum m_target, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLRENDERBUFFERSTORAGEEXTPROC) (GLenum m_target, GLenum internalformat, GLsizei width, GLsizei height); +typedef void (APIENTRYP PFNGLFRAMEBUFFERRENDERBUFFEREXTPROC) (GLenum m_target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer); +typedef void (APIENTRYP PFNGLDELETERENDERBUFFERSEXTPROC) (GLsizei n, const GLuint *renderbuffers); +#endif + +// make sure the extensions compile OK everywhere +#ifndef GL_TEXTURE_STORAGE_HINT_APPLE +#define GL_TEXTURE_STORAGE_HINT_APPLE 0x85bc +#endif + +#ifndef GL_STORAGE_CACHED_APPLE +#define GL_STORAGE_CACHED_APPLE 0x85be +#endif + +#ifndef GL_UNPACK_CLIENT_STORAGE_APPLE +#define GL_UNPACK_CLIENT_STORAGE_APPLE 0x85b2 +#endif + +#ifndef GL_TEXTURE_RECTANGLE_ARB +#define GL_TEXTURE_RECTANGLE_ARB 0x84F5 +#endif + +#ifndef GL_PIXEL_UNPACK_BUFFER_ARB +#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC +#endif + +#ifndef GL_STREAM_DRAW +#define GL_STREAM_DRAW 0x88E0 +#endif + +#ifndef GL_WRITE_ONLY +#define GL_WRITE_ONLY 0x88B9 +#endif + +#ifndef GL_ARRAY_BUFFER_ARB +#define GL_ARRAY_BUFFER_ARB 0x8892 +#endif + +#ifndef GL_PIXEL_UNPACK_BUFFER_ARB +#define GL_PIXEL_UNPACK_BUFFER_ARB 0x88EC +#endif + +#ifndef GL_FRAMEBUFFER_EXT +#define GL_FRAMEBUFFER_EXT 0x8D40 +#define GL_FRAMEBUFFER_COMPLETE_EXT 0x8CD5 +#define GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT 0x8CD6 +#define GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT 0x8CD7 +#define GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT 0x8CD8 +#define GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT 0x8CD9 +#define GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT 0x8CDA +#define GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT 0x8CDB +#define GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT 0x8CDC +#define GL_FRAMEBUFFER_UNSUPPORTED_EXT 0x8CDD +#define GL_RENDERBUFFER_EXT 0x8D41 +#define GL_DEPTH_COMPONENT16 0x81A5 +#define GL_DEPTH_COMPONENT24 0x81A6 +#define GL_DEPTH_COMPONENT32 0x81A7 +#endif + +#define HASH_SIZE ((1<<10)+1) +#define OVERFLOW_SIZE (1<<10) + +// OSD headers +#ifndef OSD_WINDOWS +#include "osdsdl.h" +#include "window.h" +#else +#include "../windows/window.h" +typedef UINT64 HashT; +#endif + +//============================================================ +// 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 + +//============================================================ +// TYPES +//============================================================ + +#if defined(OSD_WINDOWS) + +class win_gl_context : public osd_gl_context +{ +public: + win_gl_context(HWND window) : osd_gl_context(), m_context(0), m_window(NULL), m_hdc(0) + { + m_error[0] = 0; + + this->pfn_wglGetProcAddress = (PROC (WINAPI *)(LPCSTR lpszProc)) GetProcAddress(m_module, "wglGetProcAddress"); + this->pfn_wglCreateContext = (HGLRC (WINAPI *)(HDC hdc)) GetProcAddress(m_module, "wglCreateContext"); + this->pfn_wglDeleteContext = (BOOL (WINAPI *)(HGLRC hglrc)) GetProcAddress(m_module, "wglDeleteContext"); + this->pfn_wglMakeCurrent = (BOOL (WINAPI *)(HDC hdc, HGLRC hglrc)) GetProcAddress(m_module, "wglMakeCurrent"); + + this->pfn_wglGetExtensionsStringEXT = (const char *(WINAPI *) (void)) pfn_wglGetProcAddress("wglGetExtensionsStringEXT"); + + if (WGLExtensionSupported("WGL_EXT_swap_control")) + { + this->pfn_wglSwapIntervalEXT = (BOOL (WINAPI *) (int)) getProcAddress("wglSwapIntervalEXT"); + this->pfn_wglGetSwapIntervalEXT = (int (WINAPI *) (void)) getProcAddress("wglGetSwapIntervalEXT"); + } + else + { + pfn_wglSwapIntervalEXT = NULL; + pfn_wglGetSwapIntervalEXT = NULL; + } + + m_hdc = GetDC(window); + if (!setupPixelFormat(m_hdc)) + { + m_context = this->pfn_wglCreateContext(m_hdc); + if (!m_context) + { + FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, GetLastError(), 0, m_error, 255, NULL); + return; + } + this->pfn_wglMakeCurrent(m_hdc, m_context); + } + } + + virtual ~win_gl_context() + { + this->pfn_wglDeleteContext(m_context); + ReleaseDC(m_window, m_hdc); + } + + virtual void MakeCurrent() + { + this->pfn_wglMakeCurrent(m_hdc, m_context); + } + + virtual const char *LastErrorMsg() + { + if (m_error[0] == 0) + return NULL; + else + return m_error; + } + + virtual void *getProcAddress(const char *proc) + { + void *ret = (void *) GetProcAddress(m_module, proc); + if (ret == NULL) + ret = (void *) this->pfn_wglGetProcAddress(proc); + return ret; + } + + virtual int SetSwapInterval(const int swap) + { + if (this->pfn_wglSwapIntervalEXT != NULL) + { + this->pfn_wglSwapIntervalEXT(swap ? 1 : 0); + } + return 0; + } + + virtual void SwapBuffer() + { + SwapBuffers(m_hdc); + //wglSwapLayerBuffers(GetDC(window().m_hwnd), WGL_SWAP_MAIN_PLANE); + } + + static void load_library() + { + m_module = LoadLibraryA("opengl32.dll"); + } + +private: + + int setupPixelFormat(HDC hDC) + { + PIXELFORMATDESCRIPTOR pfd = { + sizeof(PIXELFORMATDESCRIPTOR), /* size */ + 1, /* version */ + PFD_SUPPORT_OPENGL | + PFD_DRAW_TO_WINDOW | + PFD_DOUBLEBUFFER, /* support double-buffering */ + PFD_TYPE_RGBA, /* color type */ + 32, /* prefered color depth */ + 0, 0, 0, 0, 0, 0, /* color bits (ignored) */ + 0, /* no alpha buffer */ + 0, /* alpha bits (ignored) */ + 0, /* no accumulation buffer */ + 0, 0, 0, 0, /* accum bits (ignored) */ + 16, /* depth buffer */ + 0, /* no stencil buffer */ + 0, /* no auxiliary buffers */ + PFD_MAIN_PLANE, /* main layer */ + 0, /* reserved */ + 0, 0, 0, /* no layer, visible, damage masks */ + }; + int pixelFormat; + + pixelFormat = ChoosePixelFormat(hDC, &pfd); + if (pixelFormat == 0) { + strcpy(m_error, "ChoosePixelFormat failed"); + return 1; + } + + if (SetPixelFormat(hDC, pixelFormat, &pfd) != TRUE) { + strcpy(m_error, "SetPixelFormat failed."); + return 1; + } + return 0; + } + + bool WGLExtensionSupported(const char *extension_name) + { + //if (pfn_wglGetExtensionsStringEXT != NULL) + // printf("%s\n", this->pfn_wglGetExtensionsStringEXT()); + + if (pfn_wglGetExtensionsStringEXT != NULL && strstr(pfn_wglGetExtensionsStringEXT(), extension_name) != NULL) + return true; + else + return false; + } + + HGLRC m_context; + HWND m_window; + HDC m_hdc; + char m_error[256]; + + PROC (WINAPI *pfn_wglGetProcAddress)(LPCSTR lpszProc); + HGLRC (WINAPI *pfn_wglCreateContext)(HDC hdc); + BOOL (WINAPI *pfn_wglDeleteContext)(HGLRC hglrc); + BOOL (WINAPI *pfn_wglMakeCurrent)(HDC hdc, HGLRC hglrc); + + const char *(WINAPI *pfn_wglGetExtensionsStringEXT) (void); + BOOL (WINAPI *pfn_wglSwapIntervalEXT) (int interval); + int (WINAPI * pfn_wglGetSwapIntervalEXT) (void); + + static HMODULE m_module; +}; + +HMODULE win_gl_context::m_module; + + +#elif SDLMAME_SDL2 + +class sdl_gl_context : public osd_gl_context +{ +public: + sdl_gl_context(SDL_Window *window) : osd_gl_context(), m_context(0), m_window(window) + { + m_error[0] = 0; + m_context = SDL_GL_CreateContext(window); + if (!m_context) + { + snprintf(m_error,255, "OpenGL not supported on this driver: %s", SDL_GetError()); + } + } + virtual ~sdl_gl_context() + { + SDL_GL_DeleteContext(m_context); + } + virtual void MakeCurrent() + { + SDL_GL_MakeCurrent(m_window, m_context); + } + + virtual int SetSwapInterval(const int swap) + { + return SDL_GL_SetSwapInterval(swap); + } + + virtual const char *LastErrorMsg() + { + if (m_error[0] == 0) + return NULL; + else + return m_error; + } + virtual void *getProcAddress(const char *proc) + { + return SDL_GL_GetProcAddress(proc); + } + + virtual void SwapBuffer() + { + SDL_GL_SwapWindow(m_window); + } + +private: + SDL_GLContext m_context; + SDL_Window *m_window; + char m_error[256]; +}; + +#else +// SDL 1.2 +class sdl12_gl_context : public osd_gl_context +{ +public: + sdl12_gl_context(SDL_Surface *window) : osd_gl_context(), m_window(window) + { + m_error[0] = 0; + } + virtual ~sdl12_gl_context() + { + } + virtual void MakeCurrent() + { + } + + virtual int SetSwapInterval(const int swap) + { + // Not supported on 1.2 + return 0; + } + + virtual const char *LastErrorMsg() + { + if (m_error[0] == 0) + return NULL; + else + return m_error; + } + + virtual void *getProcAddress(const char *proc) + { + return SDL_GL_GetProcAddress(proc); + } + + virtual void SwapBuffer() + { + SDL_GL_SwapBuffers(); + } + +private: + SDL_Surface *m_window; + char m_error[256]; +}; + +#endif + +//============================================================ +// Textures +//============================================================ + +/* ogl_texture_info holds information about a texture */ +class ogl_texture_info +{ +public: + ogl_texture_info() + : hash(0), flags(0), rawwidth(0), rawheight(0), + rawwidth_create(0), rawheight_create(0), + type(0), format(0), borderpix(0), xprescale(0), yprescale(0), nocopy(0), + texture(0), texTarget(0), texpow2(0), mpass_dest_idx(0), pbo(0), data(NULL), + data_own(0), texCoordBufferName(0) + { + 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; + } + + HashT hash; // hash value for the texture (must be >= pointer size) + UINT32 flags; // rendering flags + render_texinfo texinfo; // copy of the texture info + int rawwidth, rawheight; // raw width/height of the texture + int rawwidth_create; // raw width/height, pow2 compatible, if needed + int rawheight_create; // (create and initial set the texture, not for copy!) + int type; // what type of texture are we? + int format; // texture format + int borderpix; // do we have a 1 pixel border? + int xprescale; // what is our X prescale factor? + int yprescale; // what is our Y prescale factor? + int nocopy; // must the texture date be copied? + + UINT32 texture; // OpenGL texture "name"/ID + + GLenum texTarget; // OpenGL texture target + int texpow2; // Is this texture pow2 + + UINT32 mpass_dest_idx; // Multipass dest idx [0..1] + UINT32 mpass_textureunit[2]; // texture unit names for GLSL + + UINT32 mpass_texture_mamebm[2];// Multipass OpenGL texture "name"/ID for the shader + UINT32 mpass_fbo_mamebm[2]; // framebuffer object for this texture, multipass + UINT32 mpass_texture_scrn[2]; // Multipass OpenGL texture "name"/ID for the shader + UINT32 mpass_fbo_scrn[2]; // framebuffer object for this texture, multipass + + UINT32 pbo; // pixel buffer object for this texture (DYNAMIC only!) + UINT32 *data; // pixels for the texture + int data_own; // do we own / allocated it ? + GLfloat texCoord[8]; + GLuint texCoordBufferName; + +}; + +/* sdl_info is the information about SDL for the current screen */ +class sdl_info_ogl : public osd_renderer +{ +public: + sdl_info_ogl(osd_window *window) + : osd_renderer(window, FLAG_NEEDS_OPENGL), m_blittimer(0), + m_width(0), m_height(0), + m_blit_dim(0, 0), + m_gl_context(NULL), + 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(NULL), + 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] = NULL; + 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 */ int create(); + /* virtual */ int draw(const int update); + + /* virtual */ int xy_to_render_target(const int x, const int y, int *xt, int *yt); + /* virtual */ void destroy(); + /* virtual */ render_primitive_list *get_primitives() + { +#ifdef OSD_WINDOWS + osd_dim nd = window().get_size(); +#else + osd_dim nd = window().blit_surface_size(); +#endif + if (nd != m_blit_dim) + { + m_blit_dim = nd; + notify_changed(); + } + window().target()->set_bounds(m_blit_dim.width(), m_blit_dim.height(), window().aspect()); + return &window().target()->get_primitives(); + } + + /* virtual */ void save() { } + /* virtual */ void record() { } + /* virtual */ void toggle_fsfx() { } + +private: + void destroy_all_textures(); + + void loadGLExtensions(); + void initialize_gl(); + void set_blendmode(int blendmode); + void texture_compute_type_subroutine(const render_texinfo *texsource, ogl_texture_info *texture, UINT32 flags); + void texture_compute_size_subroutine(ogl_texture_info *texture, UINT32 flags, + UINT32 width, UINT32 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 flags); + ogl_texture_info *texture_create(const render_texinfo *texsource, UINT32 flags); + int texture_shader_create(const render_texinfo *texsource, ogl_texture_info *texture, UINT32 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(); + + INT32 m_blittimer; + int m_width; + int m_height; + osd_dim m_blit_dim; + + osd_gl_context *m_gl_context; + + 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 m_texture_max_width; // texture maximum width + INT32 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 + + glsl_shader_info *m_glsl; // glsl_shader_info + + 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 m_surf_w; + INT32 m_surf_h; + GLfloat m_texVerticex[8]; +}; + +/* 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 +//============================================================ + +INLINE HashT texture_compute_hash(const render_texinfo *texture, UINT32 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 sdl_info_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; + } +} + +//============================================================ +// PROTOTYPES +//============================================================ + +// core functions + +//============================================================ +// STATIC VARIABLES +//============================================================ + +static void drawogl_exit(void); +static void load_gl_lib(running_machine &machine); + + + +// OGL 1.3 +#ifdef GL_ARB_multitexture +static PFNGLACTIVETEXTUREARBPROC pfn_glActiveTexture = NULL; +#else +static PFNGLACTIVETEXTUREPROC pfn_glActiveTexture = NULL; +#endif + +// VBO +static PFNGLGENBUFFERSPROC pfn_glGenBuffers = NULL; +static PFNGLDELETEBUFFERSPROC pfn_glDeleteBuffers = NULL; +static PFNGLBINDBUFFERPROC pfn_glBindBuffer = NULL; +static PFNGLBUFFERDATAPROC pfn_glBufferData = NULL; +static PFNGLBUFFERSUBDATAPROC pfn_glBufferSubData = NULL; + +// PBO +static PFNGLMAPBUFFERPROC pfn_glMapBuffer = NULL; +static PFNGLUNMAPBUFFERPROC pfn_glUnmapBuffer = NULL; + +// FBO +static PFNGLISFRAMEBUFFEREXTPROC pfn_glIsFramebuffer = NULL; +static PFNGLBINDFRAMEBUFFEREXTPROC pfn_glBindFramebuffer = NULL; +static PFNGLDELETEFRAMEBUFFERSEXTPROC pfn_glDeleteFramebuffers = NULL; +static PFNGLGENFRAMEBUFFERSEXTPROC pfn_glGenFramebuffers = NULL; +static PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC pfn_glCheckFramebufferStatus = NULL; +static PFNGLFRAMEBUFFERTEXTURE2DEXTPROC pfn_glFramebufferTexture2D = NULL; + +static int glsl_shader_feature = GLSL_SHADER_FEAT_PLAIN; + +//============================================================ +// Textures +//============================================================ + +static void texture_set_data(ogl_texture_info *texture, const render_texinfo *texsource, UINT32 flags); + +//============================================================ +// Static Variables +//============================================================ + +static int shown_video_info = 0; +static int dll_loaded = 0; + +//============================================================ +// drawsdl_init +//============================================================ + +static osd_renderer *drawogl_create(osd_window *window) +{ + return global_alloc(sdl_info_ogl(window)); +} + +int drawogl_init(running_machine &machine, osd_draw_callbacks *callbacks) +{ + // fill in the callbacks + callbacks->exit = drawogl_exit; + callbacks->create = drawogl_create; + + dll_loaded = 0; + + load_gl_lib(machine); +#if defined(OSD_WINDOWS) + osd_printf_verbose("Using Windows OpenGL driver\n"); +#elif SDLMAME_SDL2 + osd_printf_verbose("Using SDL multi-window OpenGL driver (SDL 2.0+)\n"); +#else + osd_printf_verbose("Using SDL single-window OpenGL driver (SDL 1.2)\n"); +#endif + + return 0; +} + +//============================================================ +// Load the OGL function addresses +//============================================================ + +static void loadgl_functions(osd_gl_context *context) +{ +#ifdef USE_DISPATCH_GL + + int err_count = 0; + + /* the following is tricky ... #func will be expanded to glBegin + * while func will be expanded to disp_p->glBegin + */ + + #define OSD_GL(ret,func,params) \ + if (!( func = (ret (APIENTRY *)params) context->getProcAddress( #func ) )) \ + { err_count++; osd_printf_error("GL function %s not found!\n", #func ); } + + #define OSD_GL_UNUSED(ret,func,params) + + #define GET_GLFUNC 1 + #include "modules/opengl/osd_opengl.h" + #undef GET_GLFUNC + + if (err_count) + fatalerror("Error loading GL library functions, giving up\n"); + +#endif +} + +//============================================================ +// Load GL library +//============================================================ + +#ifdef USE_DISPATCH_GL +osd_gl_dispatch *gl_dispatch; +#endif + +static void load_gl_lib(running_machine &machine) +{ + if (!dll_loaded) + { +#ifdef OSD_WINDOWS + win_gl_context::load_library(); +#else +#ifdef USE_DISPATCH_GL + /* + * directfb and and x11 use this env var + * SDL_VIDEO_GL_DRIVER + */ + const char *stemp; + + stemp = downcast<sdl_options &>(machine.options()).gl_lib(); + if (stemp != NULL && strcmp(stemp, OSDOPTVAL_AUTO) == 0) + stemp = NULL; + + if (SDL_GL_LoadLibrary(stemp) != 0) // Load library (default for e==NULL + { + fatalerror("Unable to load opengl library: %s\n", stemp ? stemp : "<default>"); + } + osd_printf_verbose("Loaded opengl shared library: %s\n", stemp ? stemp : "<default>"); + /* FIXME: must be freed as well */ +#endif +#endif +#ifdef USE_DISPATCH_GL + gl_dispatch = (osd_gl_dispatch *) osd_malloc(sizeof(osd_gl_dispatch)); +#endif + dll_loaded=1; + } +} + +void sdl_info_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 (!shown_video_info) + { + osd_printf_verbose("OpenGL: %s\nOpenGL: %s\nOpenGL: %s\n", vendor, (char *)glGetString(GL_RENDERER), (char *)glGetString(GL_VERSION)); + } + + m_usetexturerect = 0; + m_texpoweroftwo = 1; + m_usevbo = 0; + m_usepbo = 0; + m_usefbo = 0; + m_useglsl = 0; + + if ( video_config.allowtexturerect && + ( strstr(extstr, "GL_ARB_texture_rectangle") || strstr(extstr, "GL_EXT_texture_rectangle") ) + ) + { + has_and_allow_texturerect = 1; + if (!shown_video_info) + { + osd_printf_verbose("OpenGL: texture rectangle supported\n"); + } + } + + // does this card support non-power-of-two sized textures? (they're faster, so use them if possible) + if ( !video_config.forcepow2texture && strstr(extstr, "GL_ARB_texture_non_power_of_two")) + { + if (!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 (!shown_video_info) + { + osd_printf_verbose("OpenGL: non-power-of-2 textures supported (old method)\n"); + } + m_usetexturerect = 1; + } + else + { + if (!shown_video_info) + { + osd_printf_verbose("OpenGL: forcing power-of-2 textures (creation, not copy)\n"); + } + } + } + + if (strstr(extstr, "GL_ARB_vertex_buffer_object")) + { + m_usevbo = video_config.vbo; + if (!shown_video_info) + { + if(m_usevbo) + osd_printf_verbose("OpenGL: vertex buffer supported\n"); + else + osd_printf_verbose("OpenGL: vertex buffer supported, but disabled\n"); + } + } + + if (strstr(extstr, "GL_ARB_pixel_buffer_object")) + { + if( m_usevbo ) + { + m_usepbo = video_config.pbo; + if (!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 (!shown_video_info) + { + osd_printf_verbose("OpenGL: pixel buffers supported, but disabled due to disabled vbo\n"); + } + } + } + else + { + if (!shown_video_info) + { + osd_printf_verbose("OpenGL: pixel buffers not supported\n"); + } + } + + if (strstr(extstr, "GL_EXT_framebuffer_object")) + { + m_usefbo = 1; + if (!shown_video_info) + { + if(m_usefbo) + osd_printf_verbose("OpenGL: framebuffer object supported\n"); + else + osd_printf_verbose("OpenGL: framebuffer object not supported\n"); + } + } + + if (strstr(extstr, "GL_ARB_shader_objects") && + strstr(extstr, "GL_ARB_shading_language_100") && + strstr(extstr, "GL_ARB_vertex_shader") && + strstr(extstr, "GL_ARB_fragment_shader") + ) + { + m_useglsl = video_config.glsl; + if (!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 (!shown_video_info) + { + osd_printf_verbose("OpenGL: GLSL not supported\n"); + } + } + +#ifdef TOBEMIGRATED + if (osd_getenv(SDLENV_VMWARE) != NULL) + { + 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 (!shown_video_info) + { + osd_printf_verbose("OpenGL: max texture size %d x %d\n", m_texture_max_width, m_texture_max_height); + } + + shown_video_info = 1; + +} +//============================================================ +// sdl_info::create +// a +// a +// a +// a +// a +// a +// a +// a +// a +// a +// a +// a +// a +// a +// a +// a +// a +// a +// a +// a +// a +// a +// a +// a +// a +// a +//============================================================ + +int sdl_info_ogl::create() +{ + // create renderer +#if defined(OSD_WINDOWS) + m_gl_context = global_alloc(win_gl_context(window().m_hwnd)); +#elif SDLMAME_SDL2 + m_gl_context = global_alloc(sdl_gl_context(window().sdl_window())); +#else + m_gl_context = global_alloc(sdl12_gl_context(window().sdl_surface())); +#endif + if (m_gl_context->LastErrorMsg() != NULL) + { + osd_printf_error("%s\n", m_gl_context->LastErrorMsg()); + return 1; + } + m_gl_context->SetSwapInterval(video_config.waitvsync ? 1 : 0); + + + m_blittimer = 0; + m_surf_w = 0; + m_surf_h = 0; + + m_initialized = 0; + + // in case any textures try to come up before these are validated, + // OpenGL guarantees all implementations can handle something this size. + m_texture_max_width = 64; + m_texture_max_height = 64; + + /* load any GL function addresses + * this must be done here because we need a context + */ + loadgl_functions(m_gl_context); + initialize_gl(); + + + m_init_context = 0; + + osd_printf_verbose("Leave sdl_info_ogl::create\n"); + return 0; +} + + +//============================================================ +// sdl_info::destroy +//============================================================ + +void sdl_info_ogl::destroy() +{ + // free the memory in the window + + destroy_all_textures(); + + global_free(m_gl_context); + m_gl_context = NULL; +} + + +//============================================================ +// drawsdl_xy_to_render_target +//============================================================ + +int sdl_info_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; +} + +//============================================================ +// drawsdl_destroy_all_textures +//============================================================ + +void sdl_info_ogl::destroy_all_textures() +{ + ogl_texture_info *texture = NULL; + int lock=FALSE; + int i; + + if ( !m_initialized ) + return; + + m_gl_context->MakeCurrent(); + + if(window().m_primlist) + { + lock=TRUE; + window().m_primlist->acquire_lock(); + } + + glFinish(); + + texture_all_disable(); + glFinish(); + glDisableClientState(GL_VERTEX_ARRAY); + + i=0; + while (i<HASH_SIZE+OVERFLOW_SIZE) + { + texture = m_texhash[i]; + m_texhash[i] = NULL; + if (texture != NULL) + { + if(m_usevbo) + { + pfn_glDeleteBuffers( 1, &(texture->texCoordBufferName) ); + texture->texCoordBufferName=0; + } + + if(m_usepbo && texture->pbo) + { + pfn_glDeleteBuffers( 1, (GLuint *)&(texture->pbo) ); + texture->pbo=0; + } + + if( m_glsl_program_num > 1 ) + { + assert(m_usefbo); + pfn_glDeleteFramebuffers(2, (GLuint *)&texture->mpass_fbo_mamebm[0]); + glDeleteTextures(2, (GLuint *)&texture->mpass_texture_mamebm[0]); + } + + if ( m_glsl_program_mb2sc < m_glsl_program_num - 1 ) + { + assert(m_usefbo); + pfn_glDeleteFramebuffers(2, (GLuint *)&texture->mpass_fbo_scrn[0]); + glDeleteTextures(2, (GLuint *)&texture->mpass_texture_scrn[0]); + } + + glDeleteTextures(1, (GLuint *)&texture->texture); + if ( texture->data_own ) + { + free(texture->data); + texture->data=NULL; + texture->data_own=FALSE; + } + global_free(texture); + } + i++; + } + if ( m_useglsl ) + { + glsl_shader_free(m_glsl); + m_glsl = NULL; + } + + m_initialized = 0; + + if (lock) + window().m_primlist->release_lock(); +} +//============================================================ +// loadGLExtensions +//============================================================ + +void sdl_info_ogl::loadGLExtensions() +{ + static int _once = 1; + + // usevbo=FALSE; // You may want to switch VBO and PBO off, by uncommenting this statement + // usepbo=FALSE; // You may want to switch PBO off, by uncommenting this statement + // useglsl=FALSE; // You may want to switch GLSL off, by uncommenting this statement + + if (! m_usevbo) + { + if(m_usepbo) // should never ever happen ;-) + { + if (_once) + { + osd_printf_warning("OpenGL: PBO not supported, no VBO support. (sdlmame error)\n"); + } + m_usepbo=FALSE; + } + if(m_useglsl) // should never ever happen ;-) + { + if (_once) + { + osd_printf_warning("OpenGL: GLSL not supported, no VBO support. (sdlmame error)\n"); + } + m_useglsl=FALSE; + } + } + + // Get Pointers To The GL Functions + // VBO: + if( m_usevbo ) + { + pfn_glGenBuffers = (PFNGLGENBUFFERSPROC) m_gl_context->getProcAddress("glGenBuffers"); + pfn_glDeleteBuffers = (PFNGLDELETEBUFFERSPROC) m_gl_context->getProcAddress("glDeleteBuffers"); + pfn_glBindBuffer = (PFNGLBINDBUFFERPROC) m_gl_context->getProcAddress("glBindBuffer"); + pfn_glBufferData = (PFNGLBUFFERDATAPROC) m_gl_context->getProcAddress("glBufferData"); + pfn_glBufferSubData = (PFNGLBUFFERSUBDATAPROC) m_gl_context->getProcAddress("glBufferSubData"); + } + // PBO: + if ( m_usepbo ) + { + pfn_glMapBuffer = (PFNGLMAPBUFFERPROC) m_gl_context->getProcAddress("glMapBuffer"); + pfn_glUnmapBuffer= (PFNGLUNMAPBUFFERPROC) m_gl_context->getProcAddress("glUnmapBuffer"); + } + // FBO: + if ( m_usefbo ) + { + pfn_glIsFramebuffer = (PFNGLISFRAMEBUFFEREXTPROC) m_gl_context->getProcAddress("glIsFramebufferEXT"); + pfn_glBindFramebuffer = (PFNGLBINDFRAMEBUFFEREXTPROC) m_gl_context->getProcAddress("glBindFramebufferEXT"); + pfn_glDeleteFramebuffers = (PFNGLDELETEFRAMEBUFFERSEXTPROC) m_gl_context->getProcAddress("glDeleteFramebuffersEXT"); + pfn_glGenFramebuffers = (PFNGLGENFRAMEBUFFERSEXTPROC) m_gl_context->getProcAddress("glGenFramebuffersEXT"); + pfn_glCheckFramebufferStatus = (PFNGLCHECKFRAMEBUFFERSTATUSEXTPROC) m_gl_context->getProcAddress("glCheckFramebufferStatusEXT"); + pfn_glFramebufferTexture2D = (PFNGLFRAMEBUFFERTEXTURE2DEXTPROC) m_gl_context->getProcAddress("glFramebufferTexture2DEXT"); + } + + if ( m_usevbo && + ( !pfn_glGenBuffers || !pfn_glDeleteBuffers || + !pfn_glBindBuffer || !pfn_glBufferData || !pfn_glBufferSubData + ) ) + { + m_usepbo=FALSE; + if (_once) + { + osd_printf_warning("OpenGL: VBO not supported, missing: "); + if (!pfn_glGenBuffers) + { + osd_printf_warning("glGenBuffers, "); + } + if (!pfn_glDeleteBuffers) + { + osd_printf_warning("glDeleteBuffers"); + } + if (!pfn_glBindBuffer) + { + osd_printf_warning("glBindBuffer, "); + } + if (!pfn_glBufferData) + { + osd_printf_warning("glBufferData, "); + } + if (!pfn_glBufferSubData) + { + osd_printf_warning("glBufferSubData, "); + } + osd_printf_warning("\n"); + } + if ( m_usevbo ) + { + if (_once) + { + osd_printf_warning("OpenGL: PBO not supported, no VBO support.\n"); + } + m_usepbo=FALSE; + } + } + + if ( m_usepbo && ( !pfn_glMapBuffer || !pfn_glUnmapBuffer ) ) + { + m_usepbo=FALSE; + if (_once) + { + osd_printf_warning("OpenGL: PBO not supported, missing: "); + if (!pfn_glMapBuffer) + { + osd_printf_warning("glMapBuffer, "); + } + if (!pfn_glUnmapBuffer) + { + osd_printf_warning("glUnmapBuffer, "); + } + osd_printf_warning("\n"); + } + } + + if ( m_usefbo && + ( !pfn_glIsFramebuffer || !pfn_glBindFramebuffer || !pfn_glDeleteFramebuffers || + !pfn_glGenFramebuffers || !pfn_glCheckFramebufferStatus || !pfn_glFramebufferTexture2D + )) + { + m_usefbo=FALSE; + if (_once) + { + osd_printf_warning("OpenGL: FBO not supported, missing: "); + if (!pfn_glIsFramebuffer) + { + osd_printf_warning("pfn_glIsFramebuffer, "); + } + if (!pfn_glBindFramebuffer) + { + osd_printf_warning("pfn_glBindFramebuffer, "); + } + if (!pfn_glDeleteFramebuffers) + { + osd_printf_warning("pfn_glDeleteFramebuffers, "); + } + if (!pfn_glGenFramebuffers) + { + osd_printf_warning("pfn_glGenFramebuffers, "); + } + if (!pfn_glCheckFramebufferStatus) + { + osd_printf_warning("pfn_glCheckFramebufferStatus, "); + } + if (!pfn_glFramebufferTexture2D) + { + osd_printf_warning("pfn_glFramebufferTexture2D, "); + } + osd_printf_warning("\n"); + } + } + + if (_once) + { + if ( m_usevbo ) + { + osd_printf_verbose("OpenGL: VBO supported\n"); + } + else + { + osd_printf_warning("OpenGL: VBO not supported\n"); + } + + if ( m_usepbo ) + { + osd_printf_verbose("OpenGL: PBO supported\n"); + } + else + { + osd_printf_warning("OpenGL: PBO not supported\n"); + } + + if ( m_usefbo ) + { + osd_printf_verbose("OpenGL: FBO supported\n"); + } + else + { + osd_printf_warning("OpenGL: FBO not supported\n"); + } + } + + if ( m_useglsl ) + { + #ifdef GL_ARB_multitexture + pfn_glActiveTexture = (PFNGLACTIVETEXTUREARBPROC) m_gl_context->getProcAddress("glActiveTextureARB"); + #else + pfn_glActiveTexture = (PFNGLACTIVETEXTUREPROC) m_gl_context->getProcAddress("glActiveTexture"); + #endif + if (!pfn_glActiveTexture) + { + if (_once) + { + osd_printf_warning("OpenGL: GLSL disabled, glActiveTexture(ARB) not supported\n"); + } + m_useglsl = 0; + } + } + + if ( m_useglsl ) + { + m_glsl = glsl_shader_init(m_gl_context); + m_useglsl = (m_glsl != NULL ? 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 ) + { + int i; + video_config.filter = FALSE; + glsl_shader_feature = GLSL_SHADER_FEAT_PLAIN; + m_glsl_program_num = 0; + m_glsl_program_mb2sc = 0; + + for(i=0; i<video_config.glsl_shader_mamebm_num; i++) + { + if ( !m_usefbo && m_glsl_program_num==1 ) + { + if (_once) + { + osd_printf_verbose("OpenGL: GLSL multipass not supported, due to unsupported FBO. Skipping followup shader\n"); + } + break; + } + + if ( glsl_shader_add_mamebm(m_glsl, video_config.glsl_shader_mamebm[i], m_glsl_program_num) ) + { + osd_printf_error("OpenGL: GLSL loading mame bitmap shader %d failed (%s)\n", + i, video_config.glsl_shader_mamebm[i]); + } else { + glsl_shader_feature = GLSL_SHADER_FEAT_CUSTOM; + if (_once) + { + osd_printf_verbose("OpenGL: GLSL using mame bitmap shader filter %d: '%s'\n", + m_glsl_program_num, video_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 ( video_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(i=0; m_usefbo && m_glsl_program_num>0 && i<video_config.glsl_shader_scrn_num; i++) + { + if ( glsl_shader_add_scrn(m_glsl, video_config.glsl_shader_scrn[i], + m_glsl_program_num-1-m_glsl_program_mb2sc) ) + { + osd_printf_error("OpenGL: GLSL loading screen bitmap shader %d failed (%s)\n", + i, video_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, video_config.glsl_shader_scrn[i]); + } + m_glsl_program_num++; + } + } + + if ( 0==m_glsl_program_num && + 0 <= video_config.glsl_filter && video_config.glsl_filter < GLSL_SHADER_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 = video_config.glsl_filter; + + if (_once) + { + osd_printf_verbose("OpenGL: GLSL using shader filter '%s', idx: %d, num %d (vid filter: %d)\n", + glsl_shader_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 sdl_info_ogl::draw(const int update) +{ + render_primitive *prim; + ogl_texture_info *texture=NULL; + float vofs, hofs; + int pendingPrimitive=GL_NO_PRIMITIVE, curPrimitive=GL_NO_PRIMITIVE; + +#ifdef TOBEMIGRATED + if (video_config.novideo) + { + return 0; + } +#endif + + 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->MakeCurrent(); + + if (m_init_context) + { + // do some one-time OpenGL setup +#if SDLMAME_SDL2 + // FIXME: SRGB conversion is working on SDL2, may be of use + // when we eventually target gamma and monitor profiles. + //glEnable(GL_FRAMEBUFFER_SRGB); +#endif + 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(); + } + +#if !defined(OSD_WINDOWS) && !SDLMAME_SDL2 + // force all textures to be regenerated + destroy_all_textures(); +#endif + + 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); + + if (window().machine().options().antialias()) + { + // 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); + } + else + { + glDisable(GL_LINE_SMOOTH); + glDisable(GL_POINT_SMOOTH); + } + + // 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) + + glViewport(0.0, 0.0, (GLsizei) m_width, (GLsizei) m_height); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glOrtho(0.0, (GLdouble) m_width, (GLdouble) m_height, 0.0, 0.0, -1.0); + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + + if ( ! m_initialized ) + { + glEnableClientState(GL_VERTEX_ARRAY); + glVertexPointer(2, GL_FLOAT, 0, m_texVerticex); // no VBO, since it's too volatile + + m_initialized = 1; + } + } + + // compute centering parameters + vofs = hofs = 0.0f; + +#ifdef TOBEMIGRATED + if (video_config.centerv || video_config.centerh) + { + int ch, cw; + + ch = m_height; + cw = m_width; + + if (video_config.centerv) + { + vofs = (ch - m_blit_dim.height()) / 2.0f; + } + if (video_config.centerh) + { + hofs = (cw - m_blit_dim.width()) / 2.0f; + } + } +#else +#endif + + m_last_hofs = hofs; + m_last_vofs = vofs; + + window().m_primlist->acquire_lock(); + + // now draw + for (prim = window().m_primlist->first(); prim != NULL; prim = prim->next()) + { + int i; + + switch (prim->type) + { + /** + * Try to stay in one Begin/End block as long as possible, + * since entering and leaving one is most expensive.. + */ + case render_primitive::LINE: + #if !USE_WIN32_STYLE_LINES + // check if it's really a point + if (((prim->bounds.x1 - prim->bounds.x0) == 0) && ((prim->bounds.y1 - prim->bounds.y0) == 0)) + { + curPrimitive=GL_POINTS; + } else { + curPrimitive=GL_LINES; + } + + if(pendingPrimitive!=GL_NO_PRIMITIVE && pendingPrimitive!=curPrimitive) + { + glEnd(); + pendingPrimitive=GL_NO_PRIMITIVE; + } + + if ( pendingPrimitive==GL_NO_PRIMITIVE ) + { + set_blendmode(PRIMFLAG_GET_BLENDMODE(prim->flags)); + } + + glColor4f(prim->color.r, prim->color.g, prim->color.b, prim->color.a); + + if(pendingPrimitive!=curPrimitive) + { + glBegin(curPrimitive); + pendingPrimitive=curPrimitive; + } + + // check if it's really a point + if (curPrimitive==GL_POINTS) + { + glVertex2f(prim->bounds.x0+hofs, prim->bounds.y0+vofs); + } + else + { + glVertex2f(prim->bounds.x0+hofs, prim->bounds.y0+vofs); + glVertex2f(prim->bounds.x1+hofs, prim->bounds.y1+vofs); + } + #else + { + const line_aa_step *step = line_aa_4step; + render_bounds b0, b1; + float r, g, b, a; + float effwidth; + + // we're not gonna play fancy here. close anything pending and let's go. + if (pendingPrimitive!=GL_NO_PRIMITIVE && pendingPrimitive!=curPrimitive) + { + glEnd(); + pendingPrimitive=GL_NO_PRIMITIVE; + } + + set_blendmode(sdl, PRIMFLAG_GET_BLENDMODE(prim->flags)); + + // compute the effective width based on the direction of the line + effwidth = prim->width(); + if (effwidth < 0.5f) + effwidth = 0.5f; + + // determine the bounds of a quad to draw this line + render_line_to_quad(&prim->bounds, effwidth, &b0, &b1); + + // fix window position + b0.x0 += hofs; + b0.x1 += hofs; + b1.x0 += hofs; + b1.x1 += hofs; + b0.y0 += vofs; + b0.y1 += vofs; + b1.y0 += vofs; + b1.y1 += vofs; + + // iterate over AA steps + for (step = PRIMFLAG_GET_ANTIALIAS(prim->flags) ? line_aa_4step : line_aa_1step; step->weight != 0; step++) + { + glBegin(GL_TRIANGLE_STRIP); + + // rotate the unit vector by 135 degrees and add to point 0 + glVertex2f(b0.x0 + step->xoffs, b0.y0 + step->yoffs); + + // rotate the unit vector by -135 degrees and add to point 0 + glVertex2f(b0.x1 + step->xoffs, b0.y1 + step->yoffs); + + // rotate the unit vector by 45 degrees and add to point 1 + glVertex2f(b1.x0 + step->xoffs, b1.y0 + step->yoffs); + + // rotate the unit vector by -45 degrees and add to point 1 + glVertex2f(b1.x1 + step->xoffs, b1.y1 + step->yoffs); + + // determine the color of the line + r = (prim->color.r * step->weight); + g = (prim->color.g * step->weight); + b = (prim->color.b * step->weight); + a = (prim->color.a * 255.0f); + if (r > 1.0) r = 1.0; + if (g > 1.0) g = 1.0; + if (b > 1.0) b = 1.0; + if (a > 1.0) a = 1.0; + glColor4f(r, g, b, a); + +// texture = texture_update(window, prim, 0); +// if (texture) printf("line has texture!\n"); + + // if we have a texture to use for the vectors, use it here + #if 0 + if (d3d->vector_texture != NULL) + { + 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<m_glsl_program_num; i++) + { + if ( i==m_glsl_program_mb2sc ) + { + // i==glsl_program_mb2sc -> 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=NULL; + } + 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->SwapBuffer(); + + return 0; +} + +//============================================================ +// texture handling +//============================================================ + +static const char * texfmt_to_string[9] = { + "ARGB32", + "RGB32", + "RGB32_PALETTED", + "YUV16", + "YUV16_PALETTED", + "PALETTE16", + "RGB15", + "RGB15_PALETTE", + "PALETTE16A" + }; + +// +// Note: if you change the following array order, change the matching defines in texsrc.h +// + +enum { SDL_TEXFORMAT_SRC_EQUALS_DEST, SDL_TEXFORMAT_SRC_HAS_PALETTE }; + +static const GLint texture_copy_properties[9][2] = { + { TRUE, FALSE }, // SDL_TEXFORMAT_ARGB32 + { TRUE, FALSE }, // SDL_TEXFORMAT_RGB32 + { TRUE, TRUE }, // SDL_TEXFORMAT_RGB32_PALETTED + { FALSE, FALSE }, // SDL_TEXFORMAT_YUY16 + { FALSE, TRUE }, // SDL_TEXFORMAT_YUY16_PALETTED + { FALSE, TRUE }, // SDL_TEXFORMAT_PALETTE16 + { TRUE, FALSE }, // SDL_TEXFORMAT_RGB15 + { TRUE, TRUE }, // SDL_TEXFORMAT_RGB15_PALETTED + { FALSE, TRUE } // SDL_TEXFORMAT_PALETTE16A +}; + +//============================================================ +// drawogl_exit +//============================================================ + +static void drawogl_exit(void) +{ + int i; + + for(i=0; i<video_config.glsl_shader_mamebm_num; i++) + { + if ( NULL!=video_config.glsl_shader_mamebm[i]) + { + free(video_config.glsl_shader_mamebm[i]); + video_config.glsl_shader_mamebm[i] = NULL; + } + } + for(i=0; i<video_config.glsl_shader_scrn_num; i++) + { + if ( NULL!=video_config.glsl_shader_scrn[i]) + { + free(video_config.glsl_shader_scrn[i]); + video_config.glsl_shader_scrn[i] = NULL; + } + } +} + +//============================================================ +// 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 sdl_info_ogl::texture_compute_type_subroutine(const render_texinfo *texsource, ogl_texture_info *texture, UINT32 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; + } +} + +INLINE int get_valid_pow2_value(int v, int needPow2) +{ + return (needPow2)?gl_round_to_pow2(v):v; +} + +void sdl_info_ogl::texture_compute_size_subroutine(ogl_texture_info *texture, UINT32 flags, + UINT32 width, UINT32 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 sdl_info_ogl::texture_compute_size_type(const render_texinfo *texsource, ogl_texture_info *texture, UINT32 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) + ); + } + + // set the final values + texture->rawwidth = finalwidth; + texture->rawheight = finalheight; + texture->rawwidth_create = finalwidth_create; + texture->rawheight_create = finalheight_create; +} + +//============================================================ +// texture_create +//============================================================ + +static int gl_checkFramebufferStatus(void) +{ + GLenum status; + status=(GLenum)pfn_glCheckFramebufferStatus(GL_FRAMEBUFFER_EXT); + switch(status) { + case GL_FRAMEBUFFER_COMPLETE_EXT: + return 0; + case GL_FRAMEBUFFER_INCOMPLETE_ATTACHMENT_EXT: + osd_printf_error("GL FBO: incomplete,incomplete attachment\n"); + return -1; + case GL_FRAMEBUFFER_UNSUPPORTED_EXT: + osd_printf_error("GL FBO: Unsupported framebuffer format\n"); + return -1; + case GL_FRAMEBUFFER_INCOMPLETE_MISSING_ATTACHMENT_EXT: + osd_printf_error("GL FBO: incomplete,missing attachment\n"); + return -1; + case GL_FRAMEBUFFER_INCOMPLETE_DIMENSIONS_EXT: + osd_printf_error("GL FBO: incomplete,attached images must have same dimensions\n"); + return -1; + case GL_FRAMEBUFFER_INCOMPLETE_FORMATS_EXT: + osd_printf_error("GL FBO: incomplete,attached images must have same format\n"); + return -1; + case GL_FRAMEBUFFER_INCOMPLETE_DRAW_BUFFER_EXT: + osd_printf_error("GL FBO: incomplete,missing draw buffer\n"); + return -1; + case GL_FRAMEBUFFER_INCOMPLETE_READ_BUFFER_EXT: + osd_printf_error("GL FBO: incomplete,missing read buffer\n"); + return -1; +#ifdef GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT + case GL_FRAMEBUFFER_INCOMPLETE_DUPLICATE_ATTACHMENT_EXT: + osd_printf_error("GL FBO: incomplete, duplicate attachment\n"); + return -1; +#endif + case 0: + osd_printf_error("GL FBO: incomplete, implementation fault\n"); + return -1; + default: + osd_printf_error("GL FBO: incomplete, implementation ERROR\n"); + /* fall through */ + } + return -1; +} + +static int texture_fbo_create(UINT32 text_unit, UINT32 text_name, UINT32 fbo_name, int width, int height) +{ + pfn_glActiveTexture(text_unit); + pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, fbo_name); + glBindTexture(GL_TEXTURE_2D, text_name); + { + GLint _width, _height; + if ( gl_texture_check_size(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, + 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, &_width, &_height, 1) ) + { + osd_printf_error("cannot create fbo texture, req: %dx%d, avail: %dx%d - bail out\n", + width, height, (int)_width, (int)_height); + return -1; + } + + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, + 0, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL ); + } + // non-screen textures will never be filtered + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + + pfn_glFramebufferTexture2D(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, + GL_TEXTURE_2D, text_name, 0); + + if ( gl_checkFramebufferStatus() ) + { + osd_printf_error("FBO error fbo texture - bail out\n"); + return -1; + } + + return 0; +} + +int sdl_info_ogl::texture_shader_create(const render_texinfo *texsource, ogl_texture_info *texture, UINT32 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; i<m_glsl_program_num; i++) + { + if ( i<=m_glsl_program_mb2sc ) + { + m_glsl_program[i] = glsl_shader_get_program_mamebm(glsl_shader_feature, i); + } else { + m_glsl_program[i] = glsl_shader_get_program_scrn(i-1-m_glsl_program_mb2sc); + } + pfn_glUseProgramObjectARB(m_glsl_program[i]); + + if ( i<=m_glsl_program_mb2sc ) + { + // GL_TEXTURE0 GLSL Uniforms + uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[i], "color_texture"); + pfn_glUniform1iARB(uniform_location, 0); + GL_CHECK_ERROR_NORMAL(); + } + + { + GLfloat color_texture_sz[2] = { (GLfloat)texture->rawwidth, (GLfloat)texture->rawheight }; + uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[i], "color_texture_sz"); + pfn_glUniform2fvARB(uniform_location, 1, &(color_texture_sz[0])); + GL_CHECK_ERROR_NORMAL(); + } + + GLfloat color_texture_pow2_sz[2] = { (GLfloat)texture->rawwidth_create, (GLfloat)texture->rawheight_create }; + uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[i], "color_texture_pow2_sz"); + pfn_glUniform2fvARB(uniform_location, 1, &(color_texture_pow2_sz[0])); + GL_CHECK_ERROR_NORMAL(); + + GLfloat screen_texture_sz[2] = { (GLfloat) m_blit_dim.width(), (GLfloat) m_blit_dim.height() }; + uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[i], "screen_texture_sz"); + pfn_glUniform2fvARB(uniform_location, 1, &(screen_texture_sz[0])); + GL_CHECK_ERROR_NORMAL(); + + GLfloat screen_texture_pow2_sz[2] = { (GLfloat)surf_w_pow2, (GLfloat)surf_h_pow2 }; + uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[i], "screen_texture_pow2_sz"); + pfn_glUniform2fvARB(uniform_location, 1, &(screen_texture_pow2_sz[0])); + GL_CHECK_ERROR_NORMAL(); + } + + pfn_glUseProgramObjectARB(m_glsl_program[0]); // start with 1st shader + + if( m_glsl_program_num > 1 ) + { + // multipass mode + // GL_TEXTURE2/GL_TEXTURE3 + pfn_glGenFramebuffers(2, (GLuint *)&texture->mpass_fbo_mamebm[0]); + glGenTextures(2, (GLuint *)&texture->mpass_texture_mamebm[0]); + + for (i=0; i<2; i++) + { + if ( texture_fbo_create(texture->mpass_textureunit[i], + texture->mpass_texture_mamebm[i], + texture->mpass_fbo_mamebm[i], + texture->rawwidth_create, texture->rawheight_create) ) + { + return -1; + } + } + + pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + + osd_printf_verbose("GL texture: mpass mame-bmp 2x %dx%d (pow2 %dx%d)\n", + texture->rawwidth, texture->rawheight, texture->rawwidth_create, texture->rawheight_create); + } + + if( m_glsl_program_num > 1 && m_glsl_program_mb2sc < m_glsl_program_num - 1 ) + { + // multipass mode + // GL_TEXTURE2/GL_TEXTURE3 + pfn_glGenFramebuffers(2, (GLuint *)&texture->mpass_fbo_scrn[0]); + glGenTextures(2, (GLuint *)&texture->mpass_texture_scrn[0]); + + for (i=0; i<2; i++) + { + if ( texture_fbo_create(texture->mpass_textureunit[i], + texture->mpass_texture_scrn[i], + texture->mpass_fbo_scrn[i], + surf_w_pow2, surf_h_pow2) ) + { + return -1; + } + } + + osd_printf_verbose("GL texture: mpass screen-bmp 2x %dx%d (pow2 %dx%d)\n", + m_width, m_height, surf_w_pow2, surf_h_pow2); + } + + // GL_TEXTURE0 + // get a name for this texture + glGenTextures(1, (GLuint *)&texture->texture); + pfn_glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, texture->texture); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->rawwidth_create); + + UINT32 * dummy = NULL; + GLint _width, _height; + if ( gl_texture_check_size(GL_TEXTURE_2D, 0, GL_RGBA8, + texture->rawwidth_create, texture->rawheight_create, + 0, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, + &_width, &_height, 1) ) + { + osd_printf_error("cannot create bitmap texture, req: %dx%d, avail: %dx%d - bail out\n", + texture->rawwidth_create, texture->rawheight_create, (int)_width, (int)_height); + return -1; + } + + dummy = (UINT32 *) malloc(texture->rawwidth_create * texture->rawheight_create * sizeof(UINT32)); + memset(dummy, 0, texture->rawwidth_create * texture->rawheight_create * sizeof(UINT32)); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, + texture->rawwidth_create, texture->rawheight_create, + 0, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, dummy); + glFinish(); // should not be necessary, .. but make sure we won't access the memory after free + free(dummy); + + if ((PRIMFLAG_GET_SCREENTEX(flags)) && video_config.filter) + { + assert( glsl_shader_feature == GLSL_SHADER_FEAT_PLAIN ); + + // screen textures get the user's choice of filtering + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + else + { + // non-screen textures will never be filtered + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + + // set wrapping mode appropriately + if (texture->flags & PRIMFLAG_TEXWRAP_MASK) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + else + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + } + + GL_CHECK_ERROR_NORMAL(); + + return 0; +} + +ogl_texture_info *sdl_info_ogl::texture_create(const render_texinfo *texsource, UINT32 flags) +{ + ogl_texture_info *texture; + + // allocate a new texture + texture = global_alloc(ogl_texture_info); + + // fill in the core data + texture->hash = texture_compute_hash(texsource, flags); + texture->flags = flags; + texture->texinfo = *texsource; + texture->texinfo.seqid = -1; // force set data + if (PRIMFLAG_GET_SCREENTEX(flags)) + { + 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 != NULL) + texture->format = SDL_TEXFORMAT_RGB32_PALETTED; + else + texture->format = SDL_TEXFORMAT_RGB32; + break; + case TEXFORMAT_PALETTE16: + texture->format = SDL_TEXFORMAT_PALETTE16; + break; + case TEXFORMAT_PALETTEA16: + texture->format = SDL_TEXFORMAT_PALETTE16A; + break; + case TEXFORMAT_YUY16: + if (texsource->palette != NULL) + texture->format = SDL_TEXFORMAT_YUY16_PALETTED; + else + texture->format = SDL_TEXFORMAT_YUY16; + break; + + default: + osd_printf_error("Unknown textureformat %d\n", PRIMFLAG_GET_TEXFORMAT(flags)); + } + + // compute the size + texture_compute_size_type(texsource, texture, flags); + + texture->pbo=0; + + if ( texture->type != TEXTURE_TYPE_SHADER && m_useglsl) + { + pfn_glUseProgramObjectARB(0); // back to fixed function pipeline + } + + if ( texture->type==TEXTURE_TYPE_SHADER ) + { + if ( texture_shader_create(texsource, texture, flags) ) + { + global_free(texture); + return NULL; + } + } + 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, NULL); + + if ((PRIMFLAG_GET_SCREENTEX(flags)) && video_config.filter) + { + // screen textures get the user's choice of filtering + glTexParameteri(texture->texTarget, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameteri(texture->texTarget, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + } + else + { + // non-screen textures will never be filtered + glTexParameteri(texture->texTarget, GL_TEXTURE_MIN_FILTER, GL_NEAREST); + glTexParameteri(texture->texTarget, GL_TEXTURE_MAG_FILTER, GL_NEAREST); + } + + if( texture->texTarget==GL_TEXTURE_RECTANGLE_ARB ) + { + // texture rectangles can't wrap + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_RECTANGLE_ARB, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + } else { + // set wrapping mode appropriately + if (texture->flags & PRIMFLAG_TEXWRAP_MASK) + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); + } + else + { + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER); + glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER); + } + } + } + + if ( texture->type == TEXTURE_TYPE_DYNAMIC ) + { + assert(m_usepbo); + + // create the PBO + pfn_glGenBuffers(1, (GLuint *)&texture->pbo); + + pfn_glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, texture->pbo); + + // set up the PBO dimension, .. + pfn_glBufferData(GL_PIXEL_UNPACK_BUFFER_ARB, + texture->rawwidth * texture->rawheight * sizeof(UINT32), + NULL, GL_STREAM_DRAW); + } + + if ( !texture->nocopy && texture->type!=TEXTURE_TYPE_DYNAMIC ) + { + texture->data = (UINT32 *) malloc(texture->rawwidth* texture->rawheight * sizeof(UINT32)); + texture->data_own=TRUE; + } + + // add us to the texture list + if (m_texhash[texture->hash] == NULL) + m_texhash[texture->hash] = texture; + else + { + int i; + for (i = HASH_SIZE; i < HASH_SIZE + OVERFLOW_SIZE; i++) + if (m_texhash[i] == NULL) + { + m_texhash[i] = texture; + break; + } + assert_always(i < HASH_SIZE + OVERFLOW_SIZE, "texture hash exhausted ..."); + } + + if(m_usevbo) + { + // Generate And Bind The Texture Coordinate Buffer + pfn_glGenBuffers( 1, &(texture->texCoordBufferName) ); + pfn_glBindBuffer( GL_ARRAY_BUFFER_ARB, texture->texCoordBufferName ); + // Load The Data + pfn_glBufferData( GL_ARRAY_BUFFER_ARB, 4*2*sizeof(GLfloat), texture->texCoord, GL_STREAM_DRAW ); + glTexCoordPointer( 2, GL_FLOAT, 0, (char *) NULL ); // we are using ARB VBO buffers + } + else + { + glTexCoordPointer(2, GL_FLOAT, 0, texture->texCoord); + } + + return texture; +} + +//============================================================ +// copyline_palette16 +//============================================================ + +INLINE void copyline_palette16(UINT32 *dst, const UINT16 *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++; + for (int x2 = 0; x2 < xprescale; x2++) + *dst++ = 0xff000000 | palette[srcpix]; + } + if (xborderpix) + *dst++ = 0xff000000 | palette[*--src]; +} + + + +//============================================================ +// copyline_palettea16 +//============================================================ + +INLINE void copyline_palettea16(UINT32 *dst, const UINT16 *src, int width, const rgb_t *palette, int xborderpix, int xprescale) +{ + int x; + + assert(xborderpix == 0 || xborderpix == 1); + if (xborderpix) + *dst++ = palette[*src]; + for (x = 0; x < width; x++) + { + int srcpix = *src++; + for (int x2 = 0; x2 < xprescale; x2++) + *dst++ = palette[srcpix]; + } + if (xborderpix) + *dst++ = palette[*--src]; +} + + + +//============================================================ +// copyline_rgb32 +//============================================================ + +INLINE void copyline_rgb32(UINT32 *dst, const UINT32 *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 != NULL) + { + 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++; + for (int x2 = 0; x2 < xprescale; x2++) + { + *dst++ = 0xff000000 | palette[0x200 + srcpix.r()] | palette[0x100 + srcpix.g()] | palette[srcpix.b()]; + } + } + 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++; + + for (int x2 = 0; x2 < xprescale; x2++) + { + *dst++ = 0xff000000 | srcpix; + } + } + if (xborderpix) + *dst++ = 0xff000000 | *--src; + } +} + +//============================================================ +// copyline_argb32 +//============================================================ + +INLINE void copyline_argb32(UINT32 *dst, const UINT32 *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 != NULL) + { + 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++; + for (int x2 = 0; x2 < xprescale; x2++) + *dst++ = (srcpix & 0xff000000) | palette[0x200 + srcpix.r()] | palette[0x100 + srcpix.g()] | palette[srcpix.b()]; + } + 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; + } +} + +INLINE UINT32 ycc_to_rgb(UINT8 y, UINT8 cb, UINT8 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 +//============================================================ + +INLINE void copyline_yuy16_to_argb(UINT32 *dst, const UINT16 *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 != NULL) + { + if (xborderpix) + { + UINT16 srcpix0 = src[0]; + UINT16 srcpix1 = src[1]; + UINT8 cb = srcpix0 & 0xff; + UINT8 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 srcpix0 = *src++; + UINT16 srcpix1 = *src++; + UINT8 cb = srcpix0 & 0xff; + UINT8 cr = srcpix1 & 0xff; + for (int x2 = 0; x2 < xprescale; x2++) + *dst++ = ycc_to_rgb(palette[0x000 + (srcpix0 >> 8)], cb, cr); + for (int x2 = 0; x2 < xprescale; x2++) + *dst++ = ycc_to_rgb(palette[0x000 + (srcpix1 >> 8)], cb, cr); + } + if (xborderpix) + { + UINT16 srcpix1 = *--src; + UINT16 srcpix0 = *--src; + UINT8 cb = srcpix0 & 0xff; + UINT8 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 srcpix0 = src[0]; + UINT16 srcpix1 = src[1]; + UINT8 cb = srcpix0 & 0xff; + UINT8 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 srcpix0 = *src++; + UINT16 srcpix1 = *src++; + UINT8 cb = srcpix0 & 0xff; + UINT8 cr = srcpix1 & 0xff; + for (int x2 = 0; x2 < xprescale; x2++) + *dst++ = ycc_to_rgb(srcpix0 >> 8, cb, cr); + for (int x2 = 0; x2 < xprescale; x2++) + *dst++ = ycc_to_rgb(srcpix1 >> 8, cb, cr); + } + if (xborderpix) + { + UINT16 srcpix1 = *--src; + UINT16 srcpix0 = *--src; + UINT8 cb = srcpix0 & 0xff; + UINT8 cr = srcpix1 & 0xff; + *dst++ = ycc_to_rgb(srcpix1 >> 8, cb, cr); + *dst++ = ycc_to_rgb(srcpix1 >> 8, cb, cr); + } + } +} + +//============================================================ +// texture_set_data +//============================================================ + +static void texture_set_data(ogl_texture_info *texture, const render_texinfo *texsource, UINT32 flags) +{ + if ( texture->type == TEXTURE_TYPE_DYNAMIC ) + { + assert(texture->pbo); + assert(!texture->nocopy); + + texture->data = (UINT32 *) pfn_glMapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB, GL_WRITE_ONLY); + } + + // note that nocopy and borderpix are mutually exclusive, IOW + // they cannot be both true, thus this cannot lead to the + // borderpix code below writing to texsource->base . + if (texture->nocopy) + { + texture->data = (UINT32 *) 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)); + } + + // when necessary copy (and convert) the data + if (!texture->nocopy) + { + int y, y2; + UINT8 *dst; + + for (y = 0; y < texsource->height; y++) + { + for (y2 = 0; y2 < texture->yprescale; y2++) + { + dst = (UINT8 *)(texture->data + (y * texture->yprescale + texture->borderpix + y2) * texture->rawwidth); + + switch (PRIMFLAG_GET_TEXFORMAT(flags)) + { + case TEXFORMAT_PALETTE16: + copyline_palette16((UINT32 *)dst, (UINT16 *)texsource->base + y * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix, texture->xprescale); + break; + + case TEXFORMAT_PALETTEA16: + copyline_palettea16((UINT32 *)dst, (UINT16 *)texsource->base + y * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix, texture->xprescale); + break; + + case TEXFORMAT_RGB32: + copyline_rgb32((UINT32 *)dst, (UINT32 *)texsource->base + y * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix, texture->xprescale); + break; + + case TEXFORMAT_ARGB32: + copyline_argb32((UINT32 *)dst, (UINT32 *)texsource->base + y * texsource->rowpixels, texsource->width, texsource->palette, texture->borderpix, texture->xprescale); + break; + + case TEXFORMAT_YUY16: + copyline_yuy16_to_argb((UINT32 *)dst, (UINT16 *)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 *)texture->data + + (texsource->height + 1) * texture->rawwidth * sizeof(UINT32), + 0, + (texsource->width * texture->xprescale + 2) * sizeof(UINT32)); + } + + if ( texture->type == TEXTURE_TYPE_SHADER ) + { + pfn_glActiveTexture(GL_TEXTURE0); + glBindTexture(texture->texTarget, texture->texture); + + if (texture->nocopy) + glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->texinfo.rowpixels); + else + glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->rawwidth); + + // and upload the image + glTexSubImage2D(texture->texTarget, 0, 0, 0, texture->rawwidth, texture->rawheight, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, texture->data); + } + else if ( texture->type == TEXTURE_TYPE_DYNAMIC ) + { + glBindTexture(texture->texTarget, texture->texture); + + glPixelStorei(GL_UNPACK_ROW_LENGTH, texture->rawwidth); + + // unmap the buffer from the CPU space so it can DMA + pfn_glUnmapBuffer(GL_PIXEL_UNPACK_BUFFER_ARB); + + // kick off the DMA + glTexSubImage2D(texture->texTarget, 0, 0, 0, texture->rawwidth, texture->rawheight, + GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, NULL); + } + 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 *sdl_info_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 != NULL) + { + int i; + if (compare_texture_primitive(texture, prim)) + return texture; + for (i=HASH_SIZE; i<HASH_SIZE + OVERFLOW_SIZE; i++) + { + texture = m_texhash[i]; + if (texture != NULL && compare_texture_primitive(texture, prim)) + return texture; + } + } + return NULL; +} + +//============================================================ +// texture_update +//============================================================ + +void sdl_info_ogl::texture_coord_update(ogl_texture_info *texture, const render_primitive *prim, int shaderIdx) +{ + float ustart = 0.0f, ustop = 0.0f; // beginning/ending U coordinates + float vstart = 0.0f, vstop = 0.0f; // beginning/ending V coordinates + float du, dv; + + if ( texture->type != 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 sdl_info_ogl::texture_mpass_flip(ogl_texture_info *texture, int shaderIdx) +{ + UINT32 mpass_src_idx = texture->mpass_dest_idx; + + texture->mpass_dest_idx = (mpass_src_idx+1) % 2; + + if ( shaderIdx>0 ) + { + int uniform_location; + uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[shaderIdx], "mpass_texture"); + pfn_glUniform1iARB(uniform_location, texture->mpass_textureunit[mpass_src_idx]-GL_TEXTURE0); + GL_CHECK_ERROR_NORMAL(); + } + + pfn_glActiveTexture(texture->mpass_textureunit[mpass_src_idx]); + if ( shaderIdx<=m_glsl_program_mb2sc ) + { + glBindTexture(texture->texTarget, texture->mpass_texture_mamebm[mpass_src_idx]); + } + else + { + glBindTexture(texture->texTarget, texture->mpass_texture_scrn[mpass_src_idx]); + } + pfn_glActiveTexture(texture->mpass_textureunit[texture->mpass_dest_idx]); + glBindTexture(texture->texTarget, 0); + + pfn_glActiveTexture(texture->mpass_textureunit[texture->mpass_dest_idx]); + + if ( shaderIdx<m_glsl_program_num-1 ) + { + if ( shaderIdx>=m_glsl_program_mb2sc ) + { + glBindTexture(texture->texTarget, texture->mpass_texture_scrn[texture->mpass_dest_idx]); + pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, texture->mpass_fbo_scrn[texture->mpass_dest_idx]); + } + else + { + glBindTexture(texture->texTarget, texture->mpass_texture_mamebm[texture->mpass_dest_idx]); + pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, texture->mpass_fbo_mamebm[texture->mpass_dest_idx]); + } + + if ( shaderIdx==0 ) + { + glPushAttrib(GL_VIEWPORT_BIT); + GL_CHECK_ERROR_NORMAL(); + glViewport(0.0, 0.0, (GLsizei)texture->rawwidth, (GLsizei)texture->rawheight); + } + + if ( shaderIdx==m_glsl_program_mb2sc ) + { + assert ( m_glsl_program_mb2sc < m_glsl_program_num-1 ); + glPopAttrib(); // glViewport(0.0, 0.0, (GLsizei)window().width, (GLsizei)window().height) + GL_CHECK_ERROR_NORMAL(); + } + glClear(GL_COLOR_BUFFER_BIT); // make sure the whole texture is redrawn .. + } + else + { + glBindTexture(texture->texTarget, 0); + pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + + if ( m_glsl_program_mb2sc == m_glsl_program_num-1 ) + { + glPopAttrib(); // glViewport(0.0, 0.0, (GLsizei)window().width, (GLsizei)window().height) + GL_CHECK_ERROR_NORMAL(); + } + + pfn_glActiveTexture(GL_TEXTURE0); + glBindTexture(texture->texTarget, 0); + } +} + +void sdl_info_ogl::texture_shader_update(ogl_texture_info *texture, render_container *container, int shaderIdx) +{ + int uniform_location; + GLfloat vid_attributes[4]; + + if (container!=NULL) + { + render_container::user_settings settings; + container->get_user_settings(settings); + /* FIXME: the code below is in just for illustration issue on + * how to set shader variables. gamma, contrast and brightness are + * handled already by the core + */ + vid_attributes[0] = settings.m_gamma; + vid_attributes[1] = settings.m_contrast; + vid_attributes[2] = settings.m_brightness; + vid_attributes[3] = 0.0f; + uniform_location = pfn_glGetUniformLocationARB(m_glsl_program[shaderIdx], "vid_attributes"); + pfn_glUniform4fvARB(uniform_location, 1, &(vid_attributes[shaderIdx])); + if ( GL_CHECK_ERROR_QUIET() ) { + osd_printf_verbose("GLSL: could not set 'vid_attributes' for shader prog idx %d\n", shaderIdx); + } + } +} + +ogl_texture_info * sdl_info_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 == NULL && prim->texture.base != NULL) + { + texture = texture_create(&prim->texture, prim->flags); + } + else if (texture != NULL) + { + if ( texture->type == TEXTURE_TYPE_SHADER ) + { + pfn_glUseProgramObjectARB(m_glsl_program[shaderIdx]); // back to our shader + } + else if ( texture->type == TEXTURE_TYPE_DYNAMIC ) + { + assert ( m_usepbo ) ; + pfn_glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, texture->pbo); + glEnable(texture->texTarget); + } + else + { + glEnable(texture->texTarget); + } + } + + if (texture != NULL) + { + 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 != NULL && texture->texinfo.seqid != prim->texture.seqid) + { + texture->texinfo.seqid = prim->texture.seqid; + + // if we found it, but with a different seqid, copy the data + texture_set_data(texture, &prim->texture, prim->flags); + texBound=1; + } + } + + if (!texBound) { + glBindTexture(texture->texTarget, texture->texture); + } + texture_coord_update(texture, prim, shaderIdx); + + glEnableClientState(GL_TEXTURE_COORD_ARRAY); + if(m_usevbo) + { + pfn_glBindBuffer( GL_ARRAY_BUFFER_ARB, texture->texCoordBufferName ); + // Load The Data + pfn_glBufferSubData( GL_ARRAY_BUFFER_ARB, 0, 4*2*sizeof(GLfloat), texture->texCoord ); + glTexCoordPointer( 2, GL_FLOAT, 0, (char *) NULL ); // we are using ARB VBO buffers + } + else + { + glTexCoordPointer(2, GL_FLOAT, 0, texture->texCoord); + } + } + + return texture; +} + +void sdl_info_ogl::texture_disable(ogl_texture_info * texture) +{ + if ( texture->type == TEXTURE_TYPE_SHADER ) + { + assert ( m_useglsl ); + pfn_glUseProgramObjectARB(0); // back to fixed function pipeline + } else if ( texture->type == TEXTURE_TYPE_DYNAMIC ) + { + pfn_glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, 0); + glDisable(texture->texTarget); + } else { + glDisable(texture->texTarget); + } +} + +void sdl_info_ogl::texture_all_disable() +{ + if ( m_useglsl ) + { + pfn_glUseProgramObjectARB(0); // back to fixed function pipeline + + pfn_glActiveTexture(GL_TEXTURE3); + glBindTexture(GL_TEXTURE_2D, 0); + if ( m_usefbo ) pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + pfn_glActiveTexture(GL_TEXTURE2); + glBindTexture(GL_TEXTURE_2D, 0); + if ( m_usefbo ) pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + pfn_glActiveTexture(GL_TEXTURE1); + glBindTexture(GL_TEXTURE_2D, 0); + if ( m_usefbo ) pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + pfn_glActiveTexture(GL_TEXTURE0); + glBindTexture(GL_TEXTURE_2D, 0); + if ( m_usefbo ) pfn_glBindFramebuffer(GL_FRAMEBUFFER_EXT, 0); + } + glBindTexture(GL_TEXTURE_RECTANGLE_ARB, 0); + + if(m_usetexturerect) + { + glDisable(GL_TEXTURE_RECTANGLE_ARB); + } + glDisable(GL_TEXTURE_2D); + + glDisableClientState(GL_TEXTURE_COORD_ARRAY); + if(m_usevbo) + { + pfn_glBindBuffer( GL_ARRAY_BUFFER_ARB, 0); // unbind .. + } + if ( m_usepbo ) + { + pfn_glBindBuffer( GL_PIXEL_UNPACK_BUFFER_ARB, 0); + } +} |