Upgrade OpenGL ES renderer to 3.0, add option for MSAA (#636)

This commit is contained in:
Christian Semmler 2025-07-23 11:00:48 -07:00 committed by GitHub
parent 8f6bfe078b
commit a5a3c4ec83
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 324 additions and 213 deletions

View File

@ -14,7 +14,7 @@ endif()
if (EMSCRIPTEN) if (EMSCRIPTEN)
add_compile_options(-pthread) add_compile_options(-pthread)
add_link_options(-sALLOW_MEMORY_GROWTH=1 -sMAXIMUM_MEMORY=2gb -sUSE_PTHREADS=1 -sPROXY_TO_PTHREAD=1 -sOFFSCREENCANVAS_SUPPORT=1 -sPTHREAD_POOL_SIZE_STRICT=0 -sFORCE_FILESYSTEM=1 -sWASMFS=1 -sEXIT_RUNTIME=1) add_link_options(-sUSE_WEBGL2=1 -sMIN_WEBGL_VERSION=2 -sALLOW_MEMORY_GROWTH=1 -sMAXIMUM_MEMORY=2gb -sUSE_PTHREADS=1 -sPROXY_TO_PTHREAD=1 -sOFFSCREENCANVAS_SUPPORT=1 -sPTHREAD_POOL_SIZE_STRICT=0 -sFORCE_FILESYSTEM=1 -sWASMFS=1 -sEXIT_RUNTIME=1)
set(SDL_PTHREADS ON CACHE BOOL "Enable SDL pthreads" FORCE) set(SDL_PTHREADS ON CACHE BOOL "Enable SDL pthreads" FORCE)
endif() endif()
@ -181,6 +181,8 @@ target_include_directories(lego1 PUBLIC "$<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}/L
target_link_libraries(lego1 PRIVATE SDL3::SDL3) target_link_libraries(lego1 PRIVATE SDL3::SDL3)
target_link_libraries(lego1 PUBLIC SDL3::Headers) target_link_libraries(lego1 PUBLIC SDL3::Headers)
target_link_libraries(lego1 PRIVATE $<$<BOOL:${ISLE_USE_DX5}>:DirectX5::DirectX5>) target_link_libraries(lego1 PRIVATE $<$<BOOL:${ISLE_USE_DX5}>:DirectX5::DirectX5>)
# Allow unconditional include of miniwin/miniwind3d.h
target_link_libraries(lego1 PRIVATE miniwin-headers)
if(WIN32) if(WIN32)
set_property(TARGET lego1 PROPERTY PREFIX "") set_property(TARGET lego1 PROPERTY PREFIX "")
endif() endif()

View File

@ -182,6 +182,7 @@ IsleApp::IsleApp()
m_exclusiveFrameRate = 60.00f; m_exclusiveFrameRate = 60.00f;
m_frameRate = 100.0f; m_frameRate = 100.0f;
m_exclusiveFullScreen = FALSE; m_exclusiveFullScreen = FALSE;
m_msaaSamples = 0;
} }
// FUNCTION: ISLE 0x4011a0 // FUNCTION: ISLE 0x4011a0
@ -914,6 +915,7 @@ MxResult IsleApp::SetupWindow()
#endif #endif
window = SDL_CreateWindowWithProperties(props); window = SDL_CreateWindowWithProperties(props);
SDL_SetPointerProperty(SDL_GetWindowProperties(window), ISLE_PROP_WINDOW_CREATE_VIDEO_PARAM, &m_videoParam);
if (m_exclusiveFullScreen && m_fullScreen) { if (m_exclusiveFullScreen && m_fullScreen) {
SDL_DisplayMode closestMode; SDL_DisplayMode closestMode;
@ -1188,6 +1190,7 @@ bool IsleApp::LoadConfig()
} }
m_frameRate = (1000.0f / iniparser_getdouble(dict, "isle:Frame Delta", m_frameDelta)); m_frameRate = (1000.0f / iniparser_getdouble(dict, "isle:Frame Delta", m_frameDelta));
m_frameDelta = static_cast<int>(std::round(iniparser_getdouble(dict, "isle:Frame Delta", m_frameDelta))); m_frameDelta = static_cast<int>(std::round(iniparser_getdouble(dict, "isle:Frame Delta", m_frameDelta)));
m_videoParam.SetMSAASamples(iniparser_getint(dict, "isle:MSAA", m_msaaSamples));
const char* deviceId = iniparser_getstring(dict, "isle:3D Device ID", NULL); const char* deviceId = iniparser_getstring(dict, "isle:3D Device ID", NULL);
if (deviceId != NULL) { if (deviceId != NULL) {

View File

@ -114,6 +114,7 @@ private:
MxFloat m_exclusiveFrameRate; MxFloat m_exclusiveFrameRate;
MxFloat m_frameRate; MxFloat m_frameRate;
MxBool m_exclusiveFullScreen; MxBool m_exclusiveFullScreen;
MxU32 m_msaaSamples;
}; };
extern IsleApp* g_isle; extern IsleApp* g_isle;

View File

@ -1,7 +1,12 @@
#ifndef MINIWIN
#include <initguid.h>
#endif
#include "mxdirect3d.h" #include "mxdirect3d.h"
#include <SDL3/SDL.h> // for SDL_Log #include <SDL3/SDL.h> // for SDL_Log
#include <assert.h> #include <assert.h>
#include <miniwin/miniwind3d.h>
DECOMP_SIZE_ASSERT(MxDirect3D, 0x894) DECOMP_SIZE_ASSERT(MxDirect3D, 0x894)
@ -44,6 +49,7 @@ BOOL MxDirect3D::Create(
) )
{ {
BOOL success = FALSE; BOOL success = FALSE;
IDirect3DMiniwin* miniwind3d = nullptr;
assert(m_currentDeviceInfo); assert(m_currentDeviceInfo);
if (!MxDirectDraw::Create( if (!MxDirectDraw::Create(
@ -64,6 +70,20 @@ BOOL MxDirect3D::Create(
goto done; goto done;
} }
if (m_pDirect3d->QueryInterface(IID_IDirect3DMiniwin, (void**) &miniwind3d) == DD_OK) {
MxVideoParam* videoParam = (MxVideoParam*) SDL_GetPointerProperty(
SDL_GetWindowProperties(reinterpret_cast<SDL_Window*>(hWnd)),
ISLE_PROP_WINDOW_CREATE_VIDEO_PARAM,
nullptr
);
#ifndef MXDIRECTX_FOR_CONFIG
assert(videoParam);
#endif
if (videoParam) {
miniwind3d->RequestMSAA(videoParam->GetMSAASamples());
}
}
if (!D3DSetMode()) { if (!D3DSetMode()) {
goto done; goto done;
} }

View File

@ -1,7 +1,10 @@
#include "mxdirectxinfo.h" #include "mxdirectxinfo.h"
#include "omni/include/mxvideoparam.h"
#include <SDL3/SDL_log.h> #include <SDL3/SDL_log.h>
#include <assert.h> #include <assert.h>
#include <miniwin/miniwind3d.h>
#include <stdio.h> // for vsprintf #include <stdio.h> // for vsprintf
DECOMP_SIZE_ASSERT(MxAssignedDevice, 0xe4) DECOMP_SIZE_ASSERT(MxAssignedDevice, 0xe4)
@ -216,12 +219,28 @@ BOOL MxDeviceEnumerate::EnumDirectDrawCallback(LPGUID p_guid, LPSTR p_driverDesc
LPDIRECTDRAW lpDD = NULL; LPDIRECTDRAW lpDD = NULL;
MxDriver& newDevice = m_ddInfo.back(); MxDriver& newDevice = m_ddInfo.back();
HRESULT result = DirectDrawCreate(newDevice.m_guid, &lpDD, NULL); HRESULT result = DirectDrawCreate(newDevice.m_guid, &lpDD, NULL);
IDirect3DMiniwin* miniwind3d = nullptr;
if (result != DD_OK) { if (result != DD_OK) {
BuildErrorString("DirectDraw Create failed: %s\n", EnumerateErrorToString(result)); BuildErrorString("DirectDraw Create failed: %s\n", EnumerateErrorToString(result));
goto done; goto done;
} }
result = lpDD->QueryInterface(IID_IDirect3DMiniwin, (void**) &miniwind3d);
if (result == DD_OK) {
MxVideoParam* videoParam = (MxVideoParam*) SDL_GetPointerProperty(
SDL_GetWindowProperties(reinterpret_cast<SDL_Window*>(m_hWnd)),
ISLE_PROP_WINDOW_CREATE_VIDEO_PARAM,
nullptr
);
#ifndef MXDIRECTX_FOR_CONFIG
assert(videoParam);
#endif
if (videoParam) {
miniwind3d->RequestMSAA(videoParam->GetMSAASamples());
}
}
result = lpDD->SetCooperativeLevel(m_hWnd, DDSCL_NORMAL); result = lpDD->SetCooperativeLevel(m_hWnd, DDSCL_NORMAL);
if (result != DD_OK) { if (result != DD_OK) {
BuildErrorString("SetCooperativeLevel failed: %s\n", EnumerateErrorToString(result)); BuildErrorString("SetCooperativeLevel failed: %s\n", EnumerateErrorToString(result));

View File

@ -15,6 +15,8 @@
class MxPalette; class MxPalette;
#define ISLE_PROP_WINDOW_CREATE_VIDEO_PARAM "ISLE.window.create.videoParam"
// SIZE 0x24 // SIZE 0x24
class MxVideoParam { class MxVideoParam {
public: public:
@ -51,6 +53,9 @@ public:
// FUNCTION: BETA10 0x10141fe0 // FUNCTION: BETA10 0x10141fe0
void SetBackBuffers(MxU32 p_backBuffers) { m_backBuffers = p_backBuffers; } void SetBackBuffers(MxU32 p_backBuffers) { m_backBuffers = p_backBuffers; }
void SetMSAASamples(MxU32 p_msaaSamples) { m_msaaSamples = p_msaaSamples; }
MxU32 GetMSAASamples() { return m_msaaSamples; }
private: private:
MxRect32 m_rect; // 0x00 MxRect32 m_rect; // 0x00
MxPalette* m_palette; // 0x10 MxPalette* m_palette; // 0x10
@ -58,6 +63,7 @@ private:
MxVideoParamFlags m_flags; // 0x18 MxVideoParamFlags m_flags; // 0x18
int m_unk0x1c; // 0x1c int m_unk0x1c; // 0x1c
char* m_deviceId; // 0x20 char* m_deviceId; // 0x20
MxU32 m_msaaSamples;
}; };
#endif // MXVIDEOPARAM_H #endif // MXVIDEOPARAM_H

View File

@ -28,6 +28,7 @@ MxVideoParam::MxVideoParam(MxRect32& p_rect, MxPalette* p_palette, MxULong p_bac
m_flags = p_flags; m_flags = p_flags;
m_unk0x1c = 0; m_unk0x1c = 0;
m_deviceId = NULL; m_deviceId = NULL;
m_msaaSamples = 0;
} }
// FUNCTION: LEGO1 0x100becf0 // FUNCTION: LEGO1 0x100becf0
@ -41,6 +42,7 @@ MxVideoParam::MxVideoParam(MxVideoParam& p_videoParam)
m_unk0x1c = p_videoParam.m_unk0x1c; m_unk0x1c = p_videoParam.m_unk0x1c;
m_deviceId = NULL; m_deviceId = NULL;
SetDeviceName(p_videoParam.m_deviceId); SetDeviceName(p_videoParam.m_deviceId);
m_msaaSamples = p_videoParam.m_msaaSamples;
} }
// FUNCTION: LEGO1 0x100bed50 // FUNCTION: LEGO1 0x100bed50
@ -82,6 +84,7 @@ MxVideoParam& MxVideoParam::operator=(const MxVideoParam& p_videoParam)
m_flags = p_videoParam.m_flags; m_flags = p_videoParam.m_flags;
m_unk0x1c = p_videoParam.m_unk0x1c; m_unk0x1c = p_videoParam.m_unk0x1c;
SetDeviceName(p_videoParam.m_deviceId); SetDeviceName(p_videoParam.m_deviceId);
m_msaaSamples = p_videoParam.m_msaaSamples;
return *this; return *this;
} }

View File

@ -44,16 +44,16 @@ if(NOT WINDOWS_STORE)
message(STATUS "🧩 OpenGL 1.x support not enabled — needs OpenGL") message(STATUS "🧩 OpenGL 1.x support not enabled — needs OpenGL")
endif() endif()
find_library(OPENGL_ES2_LIBRARY NAMES GLESv2) find_library(OPENGL_ES3_LIBRARY NAMES GLESv2)
if(EMSCRIPTEN OR OPENGL_ES2_LIBRARY) if(EMSCRIPTEN OR OPENGL_ES3_LIBRARY)
message(STATUS "Found OpenGL: enabling OpenGL ES 2.x renderer") message(STATUS "Found OpenGL: enabling OpenGL ES 3.x renderer")
target_sources(miniwin PRIVATE src/d3drm/backends/opengles2/renderer.cpp) target_sources(miniwin PRIVATE src/d3drm/backends/opengles3/renderer.cpp)
list(APPEND GRAPHICS_BACKENDS USE_OPENGLES2) list(APPEND GRAPHICS_BACKENDS USE_OPENGLES3)
if(OPENGL_ES2_LIBRARY) if(OPENGL_ES3_LIBRARY)
target_link_libraries(miniwin PRIVATE ${OPENGL_ES2_LIBRARY}) target_link_libraries(miniwin PRIVATE ${OPENGL_ES3_LIBRARY})
endif() endif()
else() else()
message(STATUS "🧩 OpenGL ES 2.x support not enabled") message(STATUS "🧩 OpenGL ES 3.x support not enabled")
endif() endif()
endif() endif()

View File

@ -0,0 +1,8 @@
#pragma once
DEFINE_GUID(IID_IDirect3DMiniwin, 0xf8a97f2d, 0x9b3a, 0x4f1c, 0x9e, 0x8d, 0x6a, 0x5b, 0x4c, 0x3d, 0x2e, 0x1f);
struct IDirect3DMiniwin : virtual public IUnknown {
virtual HRESULT RequestMSAA(DWORD msaaSamples) = 0;
virtual DWORD GetMSAASamples() const = 0;
};

View File

@ -1,8 +1,8 @@
#include "d3drmrenderer_opengles2.h" #include "d3drmrenderer_opengles3.h"
#include "meshutils.h" #include "meshutils.h"
#include <GLES2/gl2.h>
#include <GLES2/gl2ext.h> #include <GLES2/gl2ext.h>
#include <GLES3/gl3.h>
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <algorithm> #include <algorithm>
#include <string> #include <string>
@ -15,20 +15,29 @@ static GLuint CompileShader(GLenum type, const char* source)
GLint success; GLint success;
glGetShaderiv(shader, GL_COMPILE_STATUS, &success); glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
if (!success) { if (!success) {
GLint logLength = 0;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0) {
std::vector<char> log(logLength);
glGetShaderInfoLog(shader, logLength, nullptr, log.data());
SDL_Log("Shader compile error: %s", log.data());
}
else {
SDL_Log("CompileShader (%s)", SDL_GetError());
}
glDeleteShader(shader); glDeleteShader(shader);
SDL_Log("CompileShader (%s)", SDL_GetError());
return 0; return 0;
} }
return shader; return shader;
} }
struct SceneLightGLES2 { struct SceneLightGLES3 {
float color[4]; float color[4];
float position[4]; float position[4];
float direction[4]; float direction[4];
}; };
Direct3DRMRenderer* OpenGLES2Renderer::Create(DWORD width, DWORD height) Direct3DRMRenderer* OpenGLES3Renderer::Create(DWORD width, DWORD height, DWORD msaaSamples)
{ {
// We have to reset the attributes here after having enumerated the // We have to reset the attributes here after having enumerated the
// OpenGL ES 2.0 renderer, or else SDL gets very confused by SDL_GL_DEPTH_SIZE // OpenGL ES 2.0 renderer, or else SDL gets very confused by SDL_GL_DEPTH_SIZE
@ -37,7 +46,7 @@ Direct3DRMRenderer* OpenGLES2Renderer::Create(DWORD width, DWORD height)
// But ResetAttributes resets it to 16. // But ResetAttributes resets it to 16.
SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24); SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_ES);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0); SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
if (!DDWindow) { if (!DDWindow) {
@ -61,18 +70,20 @@ Direct3DRMRenderer* OpenGLES2Renderer::Create(DWORD width, DWORD height)
glCullFace(GL_BACK); glCullFace(GL_BACK);
glFrontFace(GL_CW); glFrontFace(GL_CW);
const char* vertexShaderSource = R"( const char* vertexShaderSource = R"(#version 300 es
attribute vec3 a_position; precision mediump float;
attribute vec3 a_normal;
attribute vec2 a_texCoord; in vec3 a_position;
in vec3 a_normal;
in vec2 a_texCoord;
uniform mat4 u_modelViewMatrix; uniform mat4 u_modelViewMatrix;
uniform mat3 u_normalMatrix; uniform mat3 u_normalMatrix;
uniform mat4 u_projectionMatrix; uniform mat4 u_projectionMatrix;
varying vec3 v_viewPos; out vec3 v_viewPos;
varying vec3 v_normal; out vec3 v_normal;
varying vec2 v_texCoord; out vec2 v_texCoord;
void main() { void main() {
vec4 viewPos = u_modelViewMatrix * vec4(a_position, 1.0); vec4 viewPos = u_modelViewMatrix * vec4(a_position, 1.0);
@ -83,7 +94,7 @@ Direct3DRMRenderer* OpenGLES2Renderer::Create(DWORD width, DWORD height)
} }
)"; )";
const char* fragmentShaderSource = R"( const char* fragmentShaderSource = R"(#version 300 es
precision mediump float; precision mediump float;
struct SceneLight { struct SceneLight {
@ -95,22 +106,22 @@ Direct3DRMRenderer* OpenGLES2Renderer::Create(DWORD width, DWORD height)
uniform SceneLight u_lights[3]; uniform SceneLight u_lights[3];
uniform int u_lightCount; uniform int u_lightCount;
varying vec3 v_viewPos; in vec3 v_viewPos;
varying vec3 v_normal; in vec3 v_normal;
varying vec2 v_texCoord; in vec2 v_texCoord;
uniform float u_shininess; uniform float u_shininess;
uniform vec4 u_color; uniform vec4 u_color;
uniform int u_useTexture; uniform bool u_useTexture;
uniform sampler2D u_texture; uniform sampler2D u_texture;
out vec4 fragColor;
void main() { void main() {
vec3 diffuse = vec3(0.0); vec3 diffuse = vec3(0.0);
vec3 specular = vec3(0.0); vec3 specular = vec3(0.0);
for (int i = 0; i < 3; ++i) { for (int i = 0; i < u_lightCount; ++i) {
if (i >= u_lightCount) break;
vec3 lightColor = u_lights[i].color.rgb; vec3 lightColor = u_lights[i].color.rgb;
if (u_lights[i].position.w == 0.0 && u_lights[i].direction.w == 0.0) { if (u_lights[i].position.w == 0.0 && u_lights[i].direction.w == 0.0) {
@ -134,7 +145,7 @@ Direct3DRMRenderer* OpenGLES2Renderer::Create(DWORD width, DWORD height)
// Specular // Specular
if (u_shininess > 0.0 && u_lights[i].direction.w == 1.0) { if (u_shininess > 0.0 && u_lights[i].direction.w == 1.0) {
vec3 viewVec = normalize(-v_viewPos); // Assuming camera at origin vec3 viewVec = normalize(-v_viewPos);
vec3 H = normalize(lightVec + viewVec); vec3 H = normalize(lightVec + viewVec);
float dotNH = max(dot(v_normal, H), 0.0); float dotNH = max(dot(v_normal, H), 0.0);
float spec = pow(dotNH, u_shininess); float spec = pow(dotNH, u_shininess);
@ -145,13 +156,13 @@ Direct3DRMRenderer* OpenGLES2Renderer::Create(DWORD width, DWORD height)
vec4 finalColor = u_color; vec4 finalColor = u_color;
finalColor.rgb = clamp(diffuse * u_color.rgb + specular, 0.0, 1.0); finalColor.rgb = clamp(diffuse * u_color.rgb + specular, 0.0, 1.0);
if (u_useTexture != 0) { if (u_useTexture) {
vec4 texel = texture2D(u_texture, v_texCoord); vec4 texel = texture(u_texture, v_texCoord);
finalColor.rgb = clamp(texel.rgb * finalColor.rgb, 0.0, 1.0); finalColor.rgb = clamp(texel.rgb * finalColor.rgb, 0.0, 1.0);
finalColor.a = texel.a; finalColor.a = texel.a;
} }
gl_FragColor = finalColor; fragColor = finalColor;
} }
)"; )";
@ -168,12 +179,12 @@ Direct3DRMRenderer* OpenGLES2Renderer::Create(DWORD width, DWORD height)
glDeleteShader(vs); glDeleteShader(vs);
glDeleteShader(fs); glDeleteShader(fs);
return new OpenGLES2Renderer(width, height, context, shaderProgram); return new OpenGLES3Renderer(width, height, msaaSamples, context, shaderProgram);
} }
GLES2MeshCacheEntry GLES2UploadMesh(const MeshGroup& meshGroup, bool forceUV = false) GLES3MeshCacheEntry OpenGLES3Renderer::GLES3UploadMesh(const MeshGroup& meshGroup, bool forceUV)
{ {
GLES2MeshCacheEntry cache{&meshGroup, meshGroup.version}; GLES3MeshCacheEntry cache{&meshGroup, meshGroup.version};
cache.flat = meshGroup.quality == D3DRMRENDER_FLAT || meshGroup.quality == D3DRMRENDER_UNLITFLAT; cache.flat = meshGroup.quality == D3DRMRENDER_FLAT || meshGroup.quality == D3DRMRENDER_UNLITFLAT;
@ -211,18 +222,27 @@ GLES2MeshCacheEntry GLES2UploadMesh(const MeshGroup& meshGroup, bool forceUV = f
std::vector<D3DVECTOR> normals(vertices.size()); std::vector<D3DVECTOR> normals(vertices.size());
std::transform(vertices.begin(), vertices.end(), normals.begin(), [](const D3DRMVERTEX& v) { return v.normal; }); std::transform(vertices.begin(), vertices.end(), normals.begin(), [](const D3DRMVERTEX& v) { return v.normal; });
glGenVertexArrays(1, &cache.vao);
glBindVertexArray(cache.vao);
glGenBuffers(1, &cache.vboPositions); glGenBuffers(1, &cache.vboPositions);
glBindBuffer(GL_ARRAY_BUFFER, cache.vboPositions); glBindBuffer(GL_ARRAY_BUFFER, cache.vboPositions);
glBufferData(GL_ARRAY_BUFFER, positions.size() * sizeof(D3DVECTOR), positions.data(), GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, positions.size() * sizeof(D3DVECTOR), positions.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(m_posLoc);
glVertexAttribPointer(m_posLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glGenBuffers(1, &cache.vboNormals); glGenBuffers(1, &cache.vboNormals);
glBindBuffer(GL_ARRAY_BUFFER, cache.vboNormals); glBindBuffer(GL_ARRAY_BUFFER, cache.vboNormals);
glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(D3DVECTOR), normals.data(), GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, normals.size() * sizeof(D3DVECTOR), normals.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(m_normLoc);
glVertexAttribPointer(m_normLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
if (meshGroup.texture || forceUV) { if (meshGroup.texture || forceUV) {
glGenBuffers(1, &cache.vboTexcoords); glGenBuffers(1, &cache.vboTexcoords);
glBindBuffer(GL_ARRAY_BUFFER, cache.vboTexcoords); glBindBuffer(GL_ARRAY_BUFFER, cache.vboTexcoords);
glBufferData(GL_ARRAY_BUFFER, texcoords.size() * sizeof(TexCoord), texcoords.data(), GL_STATIC_DRAW); glBufferData(GL_ARRAY_BUFFER, texcoords.size() * sizeof(TexCoord), texcoords.data(), GL_STATIC_DRAW);
glEnableVertexAttribArray(m_texLoc);
glVertexAttribPointer(m_texLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
} }
glGenBuffers(1, &cache.ibo); glGenBuffers(1, &cache.ibo);
@ -234,6 +254,8 @@ GLES2MeshCacheEntry GLES2UploadMesh(const MeshGroup& meshGroup, bool forceUV = f
GL_STATIC_DRAW GL_STATIC_DRAW
); );
glBindVertexArray(0);
return cache; return cache;
} }
@ -262,7 +284,7 @@ bool UploadTexture(SDL_Surface* source, GLuint& outTexId, bool isUI)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
if (strstr((const char*) glGetString(GL_EXTENSIONS), "GL_EXT_texture_filter_anisotropic")) { if (SDL_GL_ExtensionSupported("GL_EXT_texture_filter_anisotropic")) {
GLfloat maxAniso = 0.0f; GLfloat maxAniso = 0.0f;
glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAniso); glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &maxAniso);
GLfloat desiredAniso = fminf(8.0f, maxAniso); GLfloat desiredAniso = fminf(8.0f, maxAniso);
@ -278,12 +300,35 @@ bool UploadTexture(SDL_Surface* source, GLuint& outTexId, bool isUI)
return true; return true;
} }
OpenGLES2Renderer::OpenGLES2Renderer(DWORD width, DWORD height, SDL_GLContext context, GLuint shaderProgram) OpenGLES3Renderer::OpenGLES3Renderer(
: m_context(context), m_shaderProgram(shaderProgram) DWORD width,
DWORD height,
DWORD msaaSamples,
SDL_GLContext context,
GLuint shaderProgram
)
: m_context(context), m_shaderProgram(shaderProgram), m_msaa(msaaSamples)
{ {
glGenFramebuffers(1, &m_fbo); glGenFramebuffers(1, &m_fbo);
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
GLint maxSamples;
glGetIntegerv(GL_MAX_SAMPLES, &maxSamples);
if (m_msaa > maxSamples) {
m_msaa = maxSamples;
}
SDL_Log(
"MSAA is %s. Requested samples: %d, active samples: %d, max samples: %d",
m_msaa > 1 ? "on" : "off",
msaaSamples,
m_msaa,
maxSamples
);
if (m_msaa > 1) {
glGenFramebuffers(1, &m_resolveFBO);
}
m_virtualWidth = width; m_virtualWidth = width;
m_virtualHeight = height; m_virtualHeight = height;
ViewportTransform viewportTransform = {1.0f, 0.0f, 0.0f}; ViewportTransform viewportTransform = {1.0f, 0.0f, 0.0f};
@ -310,14 +355,6 @@ OpenGLES2Renderer::OpenGLES2Renderer(DWORD width, DWORD height, SDL_GLContext co
} }
SDL_DestroySurface(dummySurface); SDL_DestroySurface(dummySurface);
m_uiMesh.vertices = {
{{0.0f, 0.0f, 0.0f}, {0, 0, -1}, {0.0f, 0.0f}},
{{1.0f, 0.0f, 0.0f}, {0, 0, -1}, {1.0f, 0.0f}},
{{1.0f, 1.0f, 0.0f}, {0, 0, -1}, {1.0f, 1.0f}},
{{0.0f, 1.0f, 0.0f}, {0, 0, -1}, {0.0f, 1.0f}}
};
m_uiMesh.indices = {0, 1, 2, 0, 2, 3};
m_uiMeshCache = GLES2UploadMesh(m_uiMesh, true);
m_posLoc = glGetAttribLocation(m_shaderProgram, "a_position"); m_posLoc = glGetAttribLocation(m_shaderProgram, "a_position");
m_normLoc = glGetAttribLocation(m_shaderProgram, "a_normal"); m_normLoc = glGetAttribLocation(m_shaderProgram, "a_normal");
m_texLoc = glGetAttribLocation(m_shaderProgram, "a_texCoord"); m_texLoc = glGetAttribLocation(m_shaderProgram, "a_texCoord");
@ -336,17 +373,35 @@ OpenGLES2Renderer::OpenGLES2Renderer(DWORD width, DWORD height, SDL_GLContext co
m_normalMatrixLoc = glGetUniformLocation(m_shaderProgram, "u_normalMatrix"); m_normalMatrixLoc = glGetUniformLocation(m_shaderProgram, "u_normalMatrix");
m_projectionMatrixLoc = glGetUniformLocation(m_shaderProgram, "u_projectionMatrix"); m_projectionMatrixLoc = glGetUniformLocation(m_shaderProgram, "u_projectionMatrix");
m_uiMesh.vertices = {
{{0.0f, 0.0f, 0.0f}, {0, 0, -1}, {0.0f, 0.0f}},
{{1.0f, 0.0f, 0.0f}, {0, 0, -1}, {1.0f, 0.0f}},
{{1.0f, 1.0f, 0.0f}, {0, 0, -1}, {1.0f, 1.0f}},
{{0.0f, 1.0f, 0.0f}, {0, 0, -1}, {0.0f, 1.0f}}
};
m_uiMesh.indices = {0, 1, 2, 0, 2, 3};
m_uiMeshCache = GLES3UploadMesh(m_uiMesh, true);
glUseProgram(m_shaderProgram); glUseProgram(m_shaderProgram);
} }
OpenGLES2Renderer::~OpenGLES2Renderer() OpenGLES3Renderer::~OpenGLES3Renderer()
{ {
SDL_DestroySurface(m_renderedImage); SDL_DestroySurface(m_renderedImage);
glDeleteTextures(1, &m_dummyTexture);
glDeleteProgram(m_shaderProgram); glDeleteProgram(m_shaderProgram);
glDeleteRenderbuffers(1, &m_colorTarget);
glDeleteRenderbuffers(1, &m_depthTarget);
glDeleteFramebuffers(1, &m_fbo);
if (m_msaa > 1) {
glDeleteRenderbuffers(1, &m_resolveColor);
glDeleteFramebuffers(1, &m_resolveFBO);
}
SDL_GL_DestroyContext(m_context); SDL_GL_DestroyContext(m_context);
} }
void OpenGLES2Renderer::PushLights(const SceneLight* lightsArray, size_t count) void OpenGLES3Renderer::PushLights(const SceneLight* lightsArray, size_t count)
{ {
if (count > 3) { if (count > 3) {
SDL_Log("Unsupported number of lights (%d)", static_cast<int>(count)); SDL_Log("Unsupported number of lights (%d)", static_cast<int>(count));
@ -356,21 +411,21 @@ void OpenGLES2Renderer::PushLights(const SceneLight* lightsArray, size_t count)
m_lights.assign(lightsArray, lightsArray + count); m_lights.assign(lightsArray, lightsArray + count);
} }
void OpenGLES2Renderer::SetFrustumPlanes(const Plane* frustumPlanes) void OpenGLES3Renderer::SetFrustumPlanes(const Plane* frustumPlanes)
{ {
} }
void OpenGLES2Renderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) void OpenGLES3Renderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back)
{ {
memcpy(&m_projection, projection, sizeof(D3DRMMATRIX4D)); memcpy(&m_projection, projection, sizeof(D3DRMMATRIX4D));
} }
struct TextureDestroyContextGLS2 { struct TextureDestroyContextGLS2 {
OpenGLES2Renderer* renderer; OpenGLES3Renderer* renderer;
Uint32 textureId; Uint32 textureId;
}; };
void OpenGLES2Renderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture) void OpenGLES3Renderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture)
{ {
auto* ctx = new TextureDestroyContextGLS2{this, id}; auto* ctx = new TextureDestroyContextGLS2{this, id};
texture->AddDestroyCallback( texture->AddDestroyCallback(
@ -388,7 +443,7 @@ void OpenGLES2Renderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture*
); );
} }
Uint32 OpenGLES2Renderer::GetTextureId(IDirect3DRMTexture* iTexture, bool isUI, float scaleX, float scaleY) Uint32 OpenGLES3Renderer::GetTextureId(IDirect3DRMTexture* iTexture, bool isUI, float scaleX, float scaleY)
{ {
SDL_GL_MakeCurrent(DDWindow, m_context); SDL_GL_MakeCurrent(DDWindow, m_context);
auto texture = static_cast<Direct3DRMTextureImpl*>(iTexture); auto texture = static_cast<Direct3DRMTextureImpl*>(iTexture);
@ -432,42 +487,43 @@ Uint32 OpenGLES2Renderer::GetTextureId(IDirect3DRMTexture* iTexture, bool isUI,
return (Uint32) (m_textures.size() - 1); return (Uint32) (m_textures.size() - 1);
} }
struct GLES2MeshDestroyContext { struct GLES3MeshDestroyContext {
OpenGLES2Renderer* renderer; OpenGLES3Renderer* renderer;
Uint32 id; Uint32 id;
}; };
void OpenGLES2Renderer::AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh) void OpenGLES3Renderer::AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh)
{ {
auto* ctx = new GLES2MeshDestroyContext{this, id}; auto* ctx = new GLES3MeshDestroyContext{this, id};
mesh->AddDestroyCallback( mesh->AddDestroyCallback(
[](IDirect3DRMObject*, void* arg) { [](IDirect3DRMObject*, void* arg) {
auto* ctx = static_cast<GLES2MeshDestroyContext*>(arg); auto* ctx = static_cast<GLES3MeshDestroyContext*>(arg);
auto& cache = ctx->renderer->m_meshs[ctx->id]; auto& cache = ctx->renderer->m_meshs[ctx->id];
cache.meshGroup = nullptr; cache.meshGroup = nullptr;
glDeleteBuffers(1, &cache.vboPositions); glDeleteBuffers(1, &cache.vboPositions);
glDeleteBuffers(1, &cache.vboNormals); glDeleteBuffers(1, &cache.vboNormals);
glDeleteBuffers(1, &cache.vboTexcoords); glDeleteBuffers(1, &cache.vboTexcoords);
glDeleteBuffers(1, &cache.ibo); glDeleteBuffers(1, &cache.ibo);
glDeleteVertexArrays(1, &cache.vao);
delete ctx; delete ctx;
}, },
ctx ctx
); );
} }
Uint32 OpenGLES2Renderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) Uint32 OpenGLES3Renderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup)
{ {
for (Uint32 i = 0; i < m_meshs.size(); ++i) { for (Uint32 i = 0; i < m_meshs.size(); ++i) {
auto& cache = m_meshs[i]; auto& cache = m_meshs[i];
if (cache.meshGroup == meshGroup) { if (cache.meshGroup == meshGroup) {
if (cache.version != meshGroup->version) { if (cache.version != meshGroup->version) {
cache = std::move(GLES2UploadMesh(*meshGroup)); cache = std::move(GLES3UploadMesh(*meshGroup));
} }
return i; return i;
} }
} }
auto newCache = GLES2UploadMesh(*meshGroup); auto newCache = GLES3UploadMesh(*meshGroup);
for (Uint32 i = 0; i < m_meshs.size(); ++i) { for (Uint32 i = 0; i < m_meshs.size(); ++i) {
auto& cache = m_meshs[i]; auto& cache = m_meshs[i];
@ -483,7 +539,7 @@ Uint32 OpenGLES2Renderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* mesh
return (Uint32) (m_meshs.size() - 1); return (Uint32) (m_meshs.size() - 1);
} }
HRESULT OpenGLES2Renderer::BeginFrame() HRESULT OpenGLES3Renderer::BeginFrame()
{ {
SDL_GL_MakeCurrent(DDWindow, m_context); SDL_GL_MakeCurrent(DDWindow, m_context);
m_dirty = true; m_dirty = true;
@ -495,7 +551,7 @@ HRESULT OpenGLES2Renderer::BeginFrame()
glEnable(GL_DEPTH_TEST); glEnable(GL_DEPTH_TEST);
glDepthMask(GL_TRUE); glDepthMask(GL_TRUE);
SceneLightGLES2 lightData[3]; SceneLightGLES3 lightData[3];
int lightCount = std::min(static_cast<int>(m_lights.size()), 3); int lightCount = std::min(static_cast<int>(m_lights.size()), 3);
for (int i = 0; i < lightCount; ++i) { for (int i = 0; i < lightCount; ++i) {
@ -525,14 +581,14 @@ HRESULT OpenGLES2Renderer::BeginFrame()
return DD_OK; return DD_OK;
} }
void OpenGLES2Renderer::EnableTransparency() void OpenGLES3Renderer::EnableTransparency()
{ {
glEnable(GL_BLEND); glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glDepthMask(GL_FALSE); glDepthMask(GL_FALSE);
} }
void OpenGLES2Renderer::SubmitDraw( void OpenGLES3Renderer::SubmitDraw(
DWORD meshId, DWORD meshId,
const D3DRMMATRIX4D& modelViewMatrix, const D3DRMMATRIX4D& modelViewMatrix,
const D3DRMMATRIX4D& worldMatrix, const D3DRMMATRIX4D& worldMatrix,
@ -570,28 +626,12 @@ void OpenGLES2Renderer::SubmitDraw(
glUniform1i(m_textureLoc, 0); glUniform1i(m_textureLoc, 0);
} }
glBindBuffer(GL_ARRAY_BUFFER, mesh.vboPositions); glBindVertexArray(mesh.vao);
glEnableVertexAttribArray(m_posLoc);
glVertexAttribPointer(m_posLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glBindBuffer(GL_ARRAY_BUFFER, mesh.vboNormals);
glEnableVertexAttribArray(m_normLoc);
glVertexAttribPointer(m_normLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
if (appearance.textureId != NO_TEXTURE_ID) {
glBindBuffer(GL_ARRAY_BUFFER, mesh.vboTexcoords);
glEnableVertexAttribArray(m_texLoc);
glVertexAttribPointer(m_texLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
}
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.ibo);
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mesh.indices.size()), GL_UNSIGNED_SHORT, nullptr); glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(mesh.indices.size()), GL_UNSIGNED_SHORT, nullptr);
glBindVertexArray(0);
glDisableVertexAttribArray(m_normLoc);
glDisableVertexAttribArray(m_texLoc);
} }
HRESULT OpenGLES2Renderer::FinalizeFrame() HRESULT OpenGLES3Renderer::FinalizeFrame()
{ {
glBindBuffer(GL_ARRAY_BUFFER, 0); glBindBuffer(GL_ARRAY_BUFFER, 0);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0);
@ -599,7 +639,7 @@ HRESULT OpenGLES2Renderer::FinalizeFrame()
return DD_OK; return DD_OK;
} }
void OpenGLES2Renderer::Resize(int width, int height, const ViewportTransform& viewportTransform) void OpenGLES3Renderer::Resize(int width, int height, const ViewportTransform& viewportTransform)
{ {
SDL_GL_MakeCurrent(DDWindow, m_context); SDL_GL_MakeCurrent(DDWindow, m_context);
m_width = width; m_width = width;
@ -610,37 +650,65 @@ void OpenGLES2Renderer::Resize(int width, int height, const ViewportTransform& v
} }
m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32); m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_RGBA32);
if (m_colorTarget) {
glDeleteRenderbuffers(1, &m_colorTarget);
m_colorTarget = 0;
}
if (m_resolveColor) {
glDeleteRenderbuffers(1, &m_resolveColor);
m_resolveColor = 0;
}
if (m_depthTarget) {
glDeleteRenderbuffers(1, &m_depthTarget);
m_depthTarget = 0;
}
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
// Create color texture // Create color texture
glGenTextures(1, &m_colorTarget); glGenRenderbuffers(1, &m_colorTarget);
glBindTexture(GL_TEXTURE_2D, m_colorTarget); glBindRenderbuffer(GL_RENDERBUFFER, m_colorTarget);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); if (m_msaa > 1) {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); glRenderbufferStorageMultisample(GL_RENDERBUFFER, m_msaa, GL_RGBA8, width, height);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); }
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); else {
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, m_colorTarget, 0); }
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_colorTarget);
// Create depth renderbuffer // Create depth renderbuffer
glGenRenderbuffers(1, &m_depthTarget); glGenRenderbuffers(1, &m_depthTarget);
glBindRenderbuffer(GL_RENDERBUFFER, m_depthTarget); glBindRenderbuffer(GL_RENDERBUFFER, m_depthTarget);
if (m_msaa > 1) {
if (SDL_GL_ExtensionSupported("GL_OES_depth24")) { glRenderbufferStorageMultisample(GL_RENDERBUFFER, m_msaa, GL_DEPTH_COMPONENT24, width, height);
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, width, height);
}
else if (SDL_GL_ExtensionSupported("GL_OES_depth32")) {
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT32_OES, width, height);
} }
else { else {
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24, width, height);
} }
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthTarget); glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, m_depthTarget);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
SDL_Log("FBO incomplete: 0x%X", status);
}
if (m_msaa > 1) {
glBindFramebuffer(GL_FRAMEBUFFER, m_resolveFBO);
glGenRenderbuffers(1, &m_resolveColor);
glBindRenderbuffer(GL_RENDERBUFFER, m_resolveColor);
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, m_resolveColor);
GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (status != GL_FRAMEBUFFER_COMPLETE) {
SDL_Log("Resolve FBO incomplete: 0x%X", status);
}
}
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
glViewport(0, 0, m_width, m_height); glViewport(0, 0, m_width, m_height);
} }
void OpenGLES2Renderer::Clear(float r, float g, float b) void OpenGLES3Renderer::Clear(float r, float g, float b)
{ {
SDL_GL_MakeCurrent(DDWindow, m_context); SDL_GL_MakeCurrent(DDWindow, m_context);
m_dirty = true; m_dirty = true;
@ -653,71 +721,22 @@ void OpenGLES2Renderer::Clear(float r, float g, float b)
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
} }
void OpenGLES2Renderer::Flip() void OpenGLES3Renderer::Flip()
{ {
SDL_GL_MakeCurrent(DDWindow, m_context); SDL_GL_MakeCurrent(DDWindow, m_context);
if (!m_dirty) { if (!m_dirty) {
return; return;
} }
glBindFramebuffer(GL_FRAMEBUFFER, 0); glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
glDisable(GL_DEPTH_TEST); glBlitFramebuffer(0, 0, m_width, m_height, 0, 0, m_width, m_height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glFrontFace(GL_CCW);
glDepthMask(GL_FALSE);
glUniform4f(m_colorLoc, 1.0f, 1.0f, 1.0f, 1.0f);
glUniform1f(m_shinLoc, 0.0f);
float ambient[] = {1.0f, 1.0f, 1.0f, 1.0f};
float blank[] = {0.0f, 0.0f, 0.0f, 0.0f};
glUniform4fv(u_lightLocs[0][0], 1, ambient);
glUniform4fv(u_lightLocs[0][1], 1, blank);
glUniform4fv(u_lightLocs[0][2], 1, blank);
glUniform1i(m_lightCountLoc, 1);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_colorTarget);
glUniform1i(m_textureLoc, 0);
glUniform1i(m_useTextureLoc, 1);
D3DRMMATRIX4D projection;
D3DRMMATRIX4D modelViewMatrix = {
{(float) m_width, 0.0f, 0.0f, 0.0f},
{0.0f, (float) -m_height, 0.0f, 0.0f},
{0.0f, 0.0f, 1.0f, 0.0f},
{0.0f, (float) m_height, 0.0f, 1.0f}
};
glUniformMatrix4fv(m_modelViewMatrixLoc, 1, GL_FALSE, &modelViewMatrix[0][0]);
Matrix3x3 identity = {{1.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {0.f, 0.f, 1.f}};
glUniformMatrix3fv(m_normalMatrixLoc, 1, GL_FALSE, &identity[0][0]);
CreateOrthographicProjection((float) m_width, (float) m_height, projection);
glUniformMatrix4fv(m_projectionMatrixLoc, 1, GL_FALSE, &projection[0][0]);
glDisable(GL_SCISSOR_TEST);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glBindBuffer(GL_ARRAY_BUFFER, m_uiMeshCache.vboPositions);
glEnableVertexAttribArray(m_posLoc);
glVertexAttribPointer(m_posLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glBindBuffer(GL_ARRAY_BUFFER, m_uiMeshCache.vboTexcoords);
glEnableVertexAttribArray(m_texLoc);
glVertexAttribPointer(m_texLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_uiMeshCache.ibo);
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(m_uiMeshCache.indices.size()), GL_UNSIGNED_SHORT, nullptr);
glDisableVertexAttribArray(m_texLoc);
SDL_GL_SwapWindow(DDWindow); SDL_GL_SwapWindow(DDWindow);
glFrontFace(GL_CW);
m_dirty = false; m_dirty = false;
} }
void OpenGLES2Renderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect, FColor color) void OpenGLES3Renderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect, FColor color)
{ {
SDL_GL_MakeCurrent(DDWindow, m_context); SDL_GL_MakeCurrent(DDWindow, m_context);
m_dirty = true; m_dirty = true;
@ -739,7 +758,7 @@ void OpenGLES2Renderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, c
SDL_Rect expandedDstRect; SDL_Rect expandedDstRect;
if (textureId != NO_TEXTURE_ID) { if (textureId != NO_TEXTURE_ID) {
const GLES2TextureCacheEntry& texture = m_textures[textureId]; const GLES3TextureCacheEntry& texture = m_textures[textureId];
float scaleX = static_cast<float>(dstRect.w) / srcRect.w; float scaleX = static_cast<float>(dstRect.w) / srcRect.w;
float scaleY = static_cast<float>(dstRect.h) / srcRect.h; float scaleY = static_cast<float>(dstRect.h) / srcRect.h;
expandedDstRect = { expandedDstRect = {
@ -790,26 +809,27 @@ void OpenGLES2Renderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, c
static_cast<int>(std::round(dstRect.h * m_viewportTransform.scale)) static_cast<int>(std::round(dstRect.h * m_viewportTransform.scale))
); );
glBindBuffer(GL_ARRAY_BUFFER, m_uiMeshCache.vboPositions); glBindVertexArray(m_uiMeshCache.vao);
glEnableVertexAttribArray(m_posLoc);
glVertexAttribPointer(m_posLoc, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
glBindBuffer(GL_ARRAY_BUFFER, m_uiMeshCache.vboTexcoords);
glEnableVertexAttribArray(m_texLoc);
glVertexAttribPointer(m_texLoc, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_uiMeshCache.ibo);
glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(m_uiMeshCache.indices.size()), GL_UNSIGNED_SHORT, nullptr); glDrawElements(GL_TRIANGLES, static_cast<GLsizei>(m_uiMeshCache.indices.size()), GL_UNSIGNED_SHORT, nullptr);
glBindVertexArray(0);
glDisableVertexAttribArray(m_texLoc);
glDisable(GL_SCISSOR_TEST); glDisable(GL_SCISSOR_TEST);
} }
void OpenGLES2Renderer::Download(SDL_Surface* target) void OpenGLES3Renderer::Download(SDL_Surface* target)
{ {
glFinish(); glFinish();
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo); if (m_msaa > 1) {
glBindFramebuffer(GL_READ_FRAMEBUFFER, m_fbo);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, m_resolveFBO);
glBlitFramebuffer(0, 0, m_width, m_height, 0, 0, m_width, m_height, GL_COLOR_BUFFER_BIT, GL_NEAREST);
glBindFramebuffer(GL_FRAMEBUFFER, m_resolveFBO);
}
else {
glBindFramebuffer(GL_FRAMEBUFFER, m_fbo);
}
glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, m_renderedImage->pixels); glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, m_renderedImage->pixels);
SDL_Rect srcRect = { SDL_Rect srcRect = {
@ -839,7 +859,7 @@ void OpenGLES2Renderer::Download(SDL_Surface* target)
SDL_DestroySurface(bufferClone); SDL_DestroySurface(bufferClone);
} }
void OpenGLES2Renderer::SetDither(bool dither) void OpenGLES3Renderer::SetDither(bool dither)
{ {
if (dither) { if (dither) {
glEnable(GL_DITHER); glEnable(GL_DITHER);

View File

@ -132,7 +132,11 @@ HRESULT Direct3DRMImpl::CreateDeviceFromSurface(
DDSDesc.dwSize = sizeof(DDSURFACEDESC); DDSDesc.dwSize = sizeof(DDSURFACEDESC);
surface->GetSurfaceDesc(&DDSDesc); surface->GetSurfaceDesc(&DDSDesc);
DDRenderer = CreateDirect3DRMRenderer(DDSDesc, guid); IDirect3DMiniwin* miniwind3d = nullptr;
dd->QueryInterface(IID_IDirect3DMiniwin, (void**) &miniwind3d);
SDL_assert(miniwind3d);
DDRenderer = CreateDirect3DRMRenderer(miniwind3d, DDSDesc, guid);
if (!DDRenderer) { if (!DDRenderer) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Device GUID not recognized"); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Device GUID not recognized");
return E_NOINTERFACE; return E_NOINTERFACE;

View File

@ -2,8 +2,8 @@
#ifdef USE_OPENGL1 #ifdef USE_OPENGL1
#include "d3drmrenderer_opengl1.h" #include "d3drmrenderer_opengl1.h"
#endif #endif
#ifdef USE_OPENGLES2 #ifdef USE_OPENGLES3
#include "d3drmrenderer_opengles2.h" #include "d3drmrenderer_opengles3.h"
#endif #endif
#ifdef USE_CITRO3D #ifdef USE_CITRO3D
#include "d3drmrenderer_citro3d.h" #include "d3drmrenderer_citro3d.h"
@ -18,7 +18,11 @@
#include "d3drmrenderer_software.h" #include "d3drmrenderer_software.h"
#endif #endif
Direct3DRMRenderer* CreateDirect3DRMRenderer(const DDSURFACEDESC& DDSDesc, const GUID* guid) Direct3DRMRenderer* CreateDirect3DRMRenderer(
const IDirect3DMiniwin* d3d,
const DDSURFACEDESC& DDSDesc,
const GUID* guid
)
{ {
#ifdef USE_SDL_GPU #ifdef USE_SDL_GPU
if (SDL_memcmp(guid, &SDL3_GPU_GUID, sizeof(GUID)) == 0) { if (SDL_memcmp(guid, &SDL3_GPU_GUID, sizeof(GUID)) == 0) {
@ -30,9 +34,9 @@ Direct3DRMRenderer* CreateDirect3DRMRenderer(const DDSURFACEDESC& DDSDesc, const
return new Direct3DRMSoftwareRenderer(DDSDesc.dwWidth, DDSDesc.dwHeight); return new Direct3DRMSoftwareRenderer(DDSDesc.dwWidth, DDSDesc.dwHeight);
} }
#endif #endif
#ifdef USE_OPENGLES2 #ifdef USE_OPENGLES3
if (SDL_memcmp(guid, &OpenGLES2_GUID, sizeof(GUID)) == 0) { if (SDL_memcmp(guid, &OpenGLES3_GUID, sizeof(GUID)) == 0) {
return OpenGLES2Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); return OpenGLES3Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight, d3d->GetMSAASamples());
} }
#endif #endif
#ifdef USE_OPENGL1 #ifdef USE_OPENGL1
@ -53,13 +57,13 @@ Direct3DRMRenderer* CreateDirect3DRMRenderer(const DDSURFACEDESC& DDSDesc, const
return nullptr; return nullptr;
} }
void Direct3DRMRenderer_EnumDevices(LPD3DENUMDEVICESCALLBACK cb, void* ctx) void Direct3DRMRenderer_EnumDevices(const IDirect3DMiniwin* d3d, LPD3DENUMDEVICESCALLBACK cb, void* ctx)
{ {
#ifdef USE_SDL_GPU #ifdef USE_SDL_GPU
Direct3DRMSDL3GPU_EnumDevice(cb, ctx); Direct3DRMSDL3GPU_EnumDevice(cb, ctx);
#endif #endif
#ifdef USE_OPENGLES2 #ifdef USE_OPENGLES3
OpenGLES2Renderer_EnumDevice(cb, ctx); OpenGLES3Renderer_EnumDevice(d3d, cb, ctx);
#endif #endif
#ifdef USE_OPENGL1 #ifdef USE_OPENGL1
OpenGL1Renderer_EnumDevice(cb, ctx); OpenGL1Renderer_EnumDevice(cb, ctx);

View File

@ -29,6 +29,11 @@ HRESULT DirectDrawImpl::QueryInterface(const GUID& riid, void** ppvObject)
*ppvObject = static_cast<IDirect3D2*>(this); *ppvObject = static_cast<IDirect3D2*>(this);
return S_OK; return S_OK;
} }
if (SDL_memcmp(&riid, &IID_IDirect3DMiniwin, sizeof(GUID)) == 0) {
this->IUnknown::AddRef();
*ppvObject = static_cast<IDirect3DMiniwin*>(this);
return S_OK;
}
MINIWIN_NOT_IMPLEMENTED(); MINIWIN_NOT_IMPLEMENTED();
return E_NOINTERFACE; return E_NOINTERFACE;
} }
@ -220,7 +225,7 @@ void EnumDevice(
HRESULT DirectDrawImpl::EnumDevices(LPD3DENUMDEVICESCALLBACK cb, void* ctx) HRESULT DirectDrawImpl::EnumDevices(LPD3DENUMDEVICESCALLBACK cb, void* ctx)
{ {
Direct3DRMRenderer_EnumDevices(cb, ctx); Direct3DRMRenderer_EnumDevices(this, cb, ctx);
return S_OK; return S_OK;
} }
@ -317,7 +322,7 @@ HRESULT DirectDrawImpl::CreateDevice(
DDSDesc.dwSize = sizeof(DDSURFACEDESC); DDSDesc.dwSize = sizeof(DDSURFACEDESC);
pBackBuffer->GetSurfaceDesc(&DDSDesc); pBackBuffer->GetSurfaceDesc(&DDSDesc);
DDRenderer = CreateDirect3DRMRenderer(DDSDesc, &guid); DDRenderer = CreateDirect3DRMRenderer(this, DDSDesc, &guid);
if (!DDRenderer) { if (!DDRenderer) {
SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Device GUID not recognized"); SDL_LogError(SDL_LOG_CATEGORY_APPLICATION, "Device GUID not recognized");
return E_NOINTERFACE; return E_NOINTERFACE;
@ -326,6 +331,17 @@ HRESULT DirectDrawImpl::CreateDevice(
return DD_OK; return DD_OK;
} }
HRESULT DirectDrawImpl::RequestMSAA(DWORD msaaSamples)
{
m_msaaSamples = msaaSamples;
return DD_OK;
}
DWORD DirectDrawImpl::GetMSAASamples() const
{
return m_msaaSamples;
}
HRESULT DirectDrawEnumerate(LPDDENUMCALLBACKA cb, void* context) HRESULT DirectDrawEnumerate(LPDDENUMCALLBACKA cb, void* context)
{ {
const char* driverName = SDL_GetCurrentVideoDriver(); const char* driverName = SDL_GetCurrentVideoDriver();

View File

@ -3,6 +3,7 @@
#include "d3drmmesh_impl.h" #include "d3drmmesh_impl.h"
#include "mathutils.h" #include "mathutils.h"
#include "miniwin/d3drm.h" #include "miniwin/d3drm.h"
#include "miniwin/miniwind3d.h"
#include "miniwin/miniwindevice.h" #include "miniwin/miniwindevice.h"
#include "structs.h" #include "structs.h"
@ -61,5 +62,9 @@ protected:
ViewportTransform m_viewportTransform; ViewportTransform m_viewportTransform;
}; };
Direct3DRMRenderer* CreateDirect3DRMRenderer(const DDSURFACEDESC& DDSDesc, const GUID* guid); Direct3DRMRenderer* CreateDirect3DRMRenderer(
void Direct3DRMRenderer_EnumDevices(LPD3DENUMDEVICESCALLBACK cb, void* ctx); const IDirect3DMiniwin* d3d,
const DDSURFACEDESC& DDSDesc,
const GUID* guid
);
void Direct3DRMRenderer_EnumDevices(const IDirect3DMiniwin* d3d, LPD3DENUMDEVICESCALLBACK cb, void* ctx);

View File

@ -4,13 +4,13 @@
#include "d3drmtexture_impl.h" #include "d3drmtexture_impl.h"
#include "ddraw_impl.h" #include "ddraw_impl.h"
#include <GLES2/gl2.h> #include <GLES3/gl3.h>
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
#include <vector> #include <vector>
DEFINE_GUID(OpenGLES2_GUID, 0x682656F3, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04); DEFINE_GUID(OpenGLES3_GUID, 0x682656F3, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x04);
struct GLES2TextureCacheEntry { struct GLES3TextureCacheEntry {
IDirect3DRMTexture* texture; IDirect3DRMTexture* texture;
Uint32 version; Uint32 version;
GLuint glTextureId; GLuint glTextureId;
@ -18,7 +18,7 @@ struct GLES2TextureCacheEntry {
uint16_t height; uint16_t height;
}; };
struct GLES2MeshCacheEntry { struct GLES3MeshCacheEntry {
const MeshGroup* meshGroup; const MeshGroup* meshGroup;
int version; int version;
bool flat; bool flat;
@ -28,13 +28,14 @@ struct GLES2MeshCacheEntry {
GLuint vboNormals; GLuint vboNormals;
GLuint vboTexcoords; GLuint vboTexcoords;
GLuint ibo; GLuint ibo;
GLuint vao;
}; };
class OpenGLES2Renderer : public Direct3DRMRenderer { class OpenGLES3Renderer : public Direct3DRMRenderer {
public: public:
static Direct3DRMRenderer* Create(DWORD width, DWORD height); static Direct3DRMRenderer* Create(DWORD width, DWORD height, DWORD msaaSamples);
OpenGLES2Renderer(DWORD width, DWORD height, SDL_GLContext context, GLuint shaderProgram); OpenGLES3Renderer(DWORD width, DWORD height, DWORD msaaSamples, SDL_GLContext context, GLuint shaderProgram);
~OpenGLES2Renderer() override; ~OpenGLES3Renderer() override;
void PushLights(const SceneLight* lightsArray, size_t count) override; void PushLights(const SceneLight* lightsArray, size_t count) override;
void SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) override; void SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) override;
@ -62,18 +63,22 @@ public:
private: private:
void AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture); void AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture);
void AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh); void AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh);
GLES3MeshCacheEntry GLES3UploadMesh(const MeshGroup& meshGroup, bool forceUV = false);
MeshGroup m_uiMesh; MeshGroup m_uiMesh;
GLES2MeshCacheEntry m_uiMeshCache; GLES3MeshCacheEntry m_uiMeshCache;
std::vector<GLES2TextureCacheEntry> m_textures; std::vector<GLES3TextureCacheEntry> m_textures;
std::vector<GLES2MeshCacheEntry> m_meshs; std::vector<GLES3MeshCacheEntry> m_meshs;
D3DRMMATRIX4D m_projection; D3DRMMATRIX4D m_projection;
SDL_Surface* m_renderedImage = nullptr; SDL_Surface* m_renderedImage = nullptr;
bool m_dirty = false; bool m_dirty = false;
std::vector<SceneLight> m_lights; std::vector<SceneLight> m_lights;
SDL_GLContext m_context; SDL_GLContext m_context;
uint32_t m_msaa;
GLuint m_fbo; GLuint m_fbo;
GLuint m_resolveFBO;
GLuint m_colorTarget; GLuint m_colorTarget;
GLuint m_resolveColor = 0;
GLuint m_depthTarget; GLuint m_depthTarget;
GLuint m_shaderProgram; GLuint m_shaderProgram;
GLuint m_dummyTexture; GLuint m_dummyTexture;
@ -92,35 +97,25 @@ private:
ViewportTransform m_viewportTransform; ViewportTransform m_viewportTransform;
}; };
inline static void OpenGLES2Renderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx) inline static void OpenGLES3Renderer_EnumDevice(const IDirect3DMiniwin* d3d, LPD3DENUMDEVICESCALLBACK cb, void* ctx)
{ {
Direct3DRMRenderer* device = OpenGLES2Renderer::Create(640, 480); Direct3DRMRenderer* device = OpenGLES3Renderer::Create(640, 480, d3d->GetMSAASamples());
if (!device) { if (!device) {
return; return;
} }
delete device;
D3DDEVICEDESC halDesc = {}; D3DDEVICEDESC halDesc = {};
halDesc.dcmColorModel = D3DCOLOR_RGB; halDesc.dcmColorModel = D3DCOLOR_RGB;
halDesc.dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH; halDesc.dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH;
halDesc.dwDeviceZBufferBitDepth = DDBD_16; halDesc.dwDeviceZBufferBitDepth = DDBD_24;
halDesc.dwDeviceRenderBitDepth = DDBD_32; halDesc.dwDeviceRenderBitDepth = DDBD_32;
halDesc.dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE; halDesc.dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE;
halDesc.dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND; halDesc.dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND;
halDesc.dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR; halDesc.dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR;
const char* extensions = (const char*) glGetString(GL_EXTENSIONS);
if (extensions) {
if (strstr(extensions, "GL_OES_depth24")) {
halDesc.dwDeviceZBufferBitDepth |= DDBD_24;
}
if (strstr(extensions, "GL_OES_depth32")) {
halDesc.dwDeviceZBufferBitDepth |= DDBD_32;
}
}
delete device;
D3DDEVICEDESC helDesc = {}; D3DDEVICEDESC helDesc = {};
EnumDevice(cb, ctx, "OpenGL ES 2.0 HAL", &halDesc, &helDesc, OpenGLES2_GUID); EnumDevice(cb, ctx, "OpenGL ES 3.0 HAL", &halDesc, &helDesc, OpenGLES3_GUID);
} }

View File

@ -4,6 +4,7 @@
#include "framebuffer_impl.h" #include "framebuffer_impl.h"
#include "miniwin/d3d.h" #include "miniwin/d3d.h"
#include "miniwin/ddraw.h" #include "miniwin/ddraw.h"
#include "miniwin/miniwind3d.h"
#include <SDL3/SDL.h> #include <SDL3/SDL.h>
@ -15,7 +16,7 @@ inline static SDL_Rect ConvertRect(const RECT* r)
return {r->left, r->top, r->right - r->left, r->bottom - r->top}; return {r->left, r->top, r->right - r->left, r->bottom - r->top};
} }
struct DirectDrawImpl : public IDirectDraw2, public IDirect3D2 { struct DirectDrawImpl : public IDirectDraw2, public IDirect3D2, public IDirect3DMiniwin {
// IUnknown interface // IUnknown interface
HRESULT QueryInterface(const GUID& riid, void** ppvObject) override; HRESULT QueryInterface(const GUID& riid, void** ppvObject) override;
// IDirectDraw interface // IDirectDraw interface
@ -45,11 +46,15 @@ struct DirectDrawImpl : public IDirectDraw2, public IDirect3D2 {
HRESULT CreateDevice(const GUID& guid, IDirectDrawSurface* pBackBuffer, IDirect3DDevice2** ppDirect3DDevice) HRESULT CreateDevice(const GUID& guid, IDirectDrawSurface* pBackBuffer, IDirect3DDevice2** ppDirect3DDevice)
override; override;
HRESULT EnumDevices(LPD3DENUMDEVICESCALLBACK cb, void* ctx) override; HRESULT EnumDevices(LPD3DENUMDEVICESCALLBACK cb, void* ctx) override;
// IDirect3DMiniwin interface
HRESULT RequestMSAA(DWORD msaaSamples) override;
DWORD GetMSAASamples() const override;
private: private:
FrameBufferImpl* m_frameBuffer; FrameBufferImpl* m_frameBuffer;
int m_virtualWidth = 0; int m_virtualWidth = 0;
int m_virtualHeight = 0; int m_virtualHeight = 0;
DWORD m_msaaSamples = 0;
}; };
HRESULT DirectDrawEnumerate(LPDDENUMCALLBACKA cb, void* context); HRESULT DirectDrawEnumerate(LPDDENUMCALLBACKA cb, void* context);