From 2c9d9ba920ed49ac7e507dea9c1e06017fee4d4e Mon Sep 17 00:00:00 2001 From: Anders Jenbo Date: Wed, 11 Jun 2025 01:33:14 +0200 Subject: [PATCH] Upload mesh data to GPU using VBOs when available (#275) --- miniwin/CMakeLists.txt | 4 +- .../{opengl15 => opengl1}/renderer.cpp | 154 +++++++++++++----- miniwin/src/d3drm/d3drm.cpp | 10 +- miniwin/src/ddraw/ddraw.cpp | 14 +- ...rer_opengl15.h => d3drmrenderer_opengl1.h} | 23 ++- 5 files changed, 142 insertions(+), 63 deletions(-) rename miniwin/src/d3drm/backends/{opengl15 => opengl1}/renderer.cpp (72%) rename miniwin/src/internal/{d3drmrenderer_opengl15.h => d3drmrenderer_opengl1.h} (74%) diff --git a/miniwin/CMakeLists.txt b/miniwin/CMakeLists.txt index 14febf1e..ea5f7e15 100644 --- a/miniwin/CMakeLists.txt +++ b/miniwin/CMakeLists.txt @@ -29,8 +29,8 @@ add_library(miniwin STATIC EXCLUDE_FROM_ALL find_package(OpenGL) find_package(GLEW) if(OpenGL_FOUND AND GLEW_FOUND) - target_sources(miniwin PRIVATE src/d3drm/backends/opengl15/renderer.cpp) - target_compile_definitions(miniwin PRIVATE USE_OPENGL15) + target_sources(miniwin PRIVATE src/d3drm/backends/opengl1/renderer.cpp) + target_compile_definitions(miniwin PRIVATE USE_OPENGL1) # Find and link OpenGL (1.5) target_link_libraries(miniwin PRIVATE OpenGL::GL) # Glew is used for getting a FBO for off screen rendering diff --git a/miniwin/src/d3drm/backends/opengl15/renderer.cpp b/miniwin/src/d3drm/backends/opengl1/renderer.cpp similarity index 72% rename from miniwin/src/d3drm/backends/opengl15/renderer.cpp rename to miniwin/src/d3drm/backends/opengl1/renderer.cpp index 476bad75..ad5021ce 100644 --- a/miniwin/src/d3drm/backends/opengl15/renderer.cpp +++ b/miniwin/src/d3drm/backends/opengl1/renderer.cpp @@ -1,4 +1,4 @@ -#include "d3drmrenderer_opengl15.h" +#include "d3drmrenderer_opengl1.h" #include "ddraw_impl.h" #include "ddsurface_impl.h" #include "mathutils.h" @@ -9,16 +9,16 @@ #include #include -Direct3DRMRenderer* OpenGL15Renderer::Create(DWORD width, DWORD height) +Direct3DRMRenderer* OpenGL1Renderer::Create(DWORD width, DWORD height) { SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 1); - SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 5); + SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 2); SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY); SDL_Window* window = DDWindow; bool testWindow = false; if (!window) { - window = SDL_CreateWindow("OpenGL 1.5 test", width, height, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); + window = SDL_CreateWindow("OpenGL 1.2 test", width, height, SDL_WINDOW_HIDDEN | SDL_WINDOW_OPENGL); testWindow = true; } @@ -39,12 +39,16 @@ Direct3DRMRenderer* OpenGL15Renderer::Create(DWORD width, DWORD height) return nullptr; } + if (!GLEW_EXT_framebuffer_object) { + if (testWindow) { + SDL_DestroyWindow(window); + } + return nullptr; + } + glEnable(GL_CULL_FACE); glCullFace(GL_BACK); glFrontFace(GL_CCW); - glEnable(GL_DEPTH_TEST); - glEnable(GL_LIGHTING); - glEnable(GL_COLOR_MATERIAL); // Setup FBO GLuint fbo; @@ -76,10 +80,10 @@ Direct3DRMRenderer* OpenGL15Renderer::Create(DWORD width, DWORD height) SDL_DestroyWindow(window); } - return new OpenGL15Renderer(width, height, context, fbo, colorTex, depthRb); + return new OpenGL1Renderer(width, height, context, fbo, colorTex, depthRb); } -OpenGL15Renderer::OpenGL15Renderer( +OpenGL1Renderer::OpenGL1Renderer( int width, int height, SDL_GLContext context, @@ -90,32 +94,33 @@ OpenGL15Renderer::OpenGL15Renderer( : m_width(width), m_height(height), m_context(context), m_fbo(fbo), m_colorTex(colorTex), m_depthRb(depthRb) { m_renderedImage = SDL_CreateSurface(m_width, m_height, SDL_PIXELFORMAT_ABGR8888); + m_useVBOs = GLEW_ARB_vertex_buffer_object; } -OpenGL15Renderer::~OpenGL15Renderer() +OpenGL1Renderer::~OpenGL1Renderer() { if (m_renderedImage) { SDL_DestroySurface(m_renderedImage); } } -void OpenGL15Renderer::PushLights(const SceneLight* lightsArray, size_t count) +void OpenGL1Renderer::PushLights(const SceneLight* lightsArray, size_t count) { m_lights.assign(lightsArray, lightsArray + count); } -void OpenGL15Renderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) +void OpenGL1Renderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) { memcpy(&m_projection, projection, sizeof(D3DRMMATRIX4D)); m_projection[1][1] *= -1.0f; // OpenGL is upside down } struct TextureDestroyContextGL { - OpenGL15Renderer* renderer; + OpenGL1Renderer* renderer; Uint32 textureId; }; -void OpenGL15Renderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture) +void OpenGL1Renderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture) { auto* ctx = new TextureDestroyContextGL{this, id}; texture->AddDestroyCallback( @@ -133,7 +138,7 @@ void OpenGL15Renderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* ); } -Uint32 OpenGL15Renderer::GetTextureId(IDirect3DRMTexture* iTexture) +Uint32 OpenGL1Renderer::GetTextureId(IDirect3DRMTexture* iTexture) { auto texture = static_cast(iTexture); auto surface = static_cast(texture->m_surface); @@ -192,7 +197,7 @@ Uint32 OpenGL15Renderer::GetTextureId(IDirect3DRMTexture* iTexture) return (Uint32) (m_textures.size() - 1); } -GLMeshCacheEntry GLUploadMesh(const MeshGroup& meshGroup) +GLMeshCacheEntry GLUploadMesh(const MeshGroup& meshGroup, bool useVBOs) { GLMeshCacheEntry cache{&meshGroup, meshGroup.version}; @@ -211,14 +216,16 @@ GLMeshCacheEntry GLUploadMesh(const MeshGroup& meshGroup) ); } else { - vertices.assign(meshGroup.vertices.begin(), meshGroup.vertices.end()); - cache.indices.assign(meshGroup.indices.begin(), meshGroup.indices.end()); + vertices = meshGroup.vertices; + cache.indices = meshGroup.indices; } - cache.texcoords.resize(vertices.size()); - std::transform(vertices.begin(), vertices.end(), cache.texcoords.begin(), [](const D3DRMVERTEX& v) { - return v.texCoord; - }); + if (meshGroup.texture != nullptr) { + cache.texcoords.resize(vertices.size()); + std::transform(vertices.begin(), vertices.end(), cache.texcoords.begin(), [](const D3DRMVERTEX& v) { + return v.texCoord; + }); + } cache.positions.resize(vertices.size()); std::transform(vertices.begin(), vertices.end(), cache.positions.begin(), [](const D3DRMVERTEX& v) { return v.position; @@ -228,40 +235,82 @@ GLMeshCacheEntry GLUploadMesh(const MeshGroup& meshGroup) return v.normal; }); + if (useVBOs) { + glGenBuffers(1, &cache.vboPositions); + glBindBuffer(GL_ARRAY_BUFFER, cache.vboPositions); + glBufferData( + GL_ARRAY_BUFFER, + cache.positions.size() * sizeof(D3DVECTOR), + cache.positions.data(), + GL_STATIC_DRAW + ); + + glGenBuffers(1, &cache.vboNormals); + glBindBuffer(GL_ARRAY_BUFFER, cache.vboNormals); + glBufferData(GL_ARRAY_BUFFER, cache.normals.size() * sizeof(D3DVECTOR), cache.normals.data(), GL_STATIC_DRAW); + + if (meshGroup.texture != nullptr) { + glGenBuffers(1, &cache.vboTexcoords); + glBindBuffer(GL_ARRAY_BUFFER, cache.vboTexcoords); + glBufferData( + GL_ARRAY_BUFFER, + cache.texcoords.size() * sizeof(TexCoord), + cache.texcoords.data(), + GL_STATIC_DRAW + ); + } + + glGenBuffers(1, &cache.ibo); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, cache.ibo); + glBufferData( + GL_ELEMENT_ARRAY_BUFFER, + cache.indices.size() * sizeof(Uint32), + cache.indices.data(), + GL_STATIC_DRAW + ); + } + return cache; } struct GLMeshDestroyContext { - OpenGL15Renderer* renderer; + OpenGL1Renderer* renderer; Uint32 id; }; -void OpenGL15Renderer::AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh) +void OpenGL1Renderer::AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh) { auto* ctx = new GLMeshDestroyContext{this, id}; mesh->AddDestroyCallback( [](IDirect3DRMObject*, void* arg) { auto* ctx = static_cast(arg); - ctx->renderer->m_meshs[ctx->id].meshGroup = nullptr; + auto& cache = ctx->renderer->m_meshs[ctx->id]; + cache.meshGroup = nullptr; + if (ctx->renderer->m_useVBOs) { + glDeleteBuffers(1, &cache.vboPositions); + glDeleteBuffers(1, &cache.vboNormals); + glDeleteBuffers(1, &cache.vboTexcoords); + glDeleteBuffers(1, &cache.ibo); + } delete ctx; }, ctx ); } -Uint32 OpenGL15Renderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) +Uint32 OpenGL1Renderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) { for (Uint32 i = 0; i < m_meshs.size(); ++i) { auto& cache = m_meshs[i]; if (cache.meshGroup == meshGroup) { if (cache.version != meshGroup->version) { - cache = std::move(GLUploadMesh(*meshGroup)); + cache = std::move(GLUploadMesh(*meshGroup, m_useVBOs)); } return i; } } - auto newCache = GLUploadMesh(*meshGroup); + auto newCache = GLUploadMesh(*meshGroup, m_useVBOs); for (Uint32 i = 0; i < m_meshs.size(); ++i) { auto& cache = m_meshs[i]; @@ -277,17 +326,17 @@ Uint32 OpenGL15Renderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshG return (Uint32) (m_meshs.size() - 1); } -DWORD OpenGL15Renderer::GetWidth() +DWORD OpenGL1Renderer::GetWidth() { return m_width; } -DWORD OpenGL15Renderer::GetHeight() +DWORD OpenGL1Renderer::GetHeight() { return m_height; } -void OpenGL15Renderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) +void OpenGL1Renderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) { halDesc->dcmColorModel = D3DCOLORMODEL::RGB; halDesc->dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH; @@ -300,12 +349,12 @@ void OpenGL15Renderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) memset(helDesc, 0, sizeof(D3DDEVICEDESC)); } -const char* OpenGL15Renderer::GetName() +const char* OpenGL1Renderer::GetName() { - return "OpenGL 1.5 HAL"; + return "OpenGL 1.2 HAL"; } -HRESULT OpenGL15Renderer::BeginFrame(const D3DRMMATRIX4D& viewMatrix) +HRESULT OpenGL1Renderer::BeginFrame(const D3DRMMATRIX4D& viewMatrix) { if (!DDBackBuffer) { return DDERR_GENERIC; @@ -390,7 +439,7 @@ HRESULT OpenGL15Renderer::BeginFrame(const D3DRMMATRIX4D& viewMatrix) return DD_OK; } -void OpenGL15Renderer::SubmitDraw( +void OpenGL1Renderer::SubmitDraw( DWORD meshId, const D3DRMMATRIX4D& worldMatrix, const Matrix3x3& normalMatrix, @@ -433,7 +482,6 @@ void OpenGL15Renderer::SubmitDraw( glEnable(GL_TEXTURE_2D); glBindTexture(GL_TEXTURE_2D, tex.glTextureId); glEnableClientState(GL_TEXTURE_COORD_ARRAY); - glTexCoordPointer(2, GL_FLOAT, 0, mesh.texcoords.data()); } else { glDisable(GL_TEXTURE_2D); @@ -441,18 +489,40 @@ void OpenGL15Renderer::SubmitDraw( } glEnableClientState(GL_VERTEX_ARRAY); - glVertexPointer(3, GL_FLOAT, 0, mesh.positions.data()); - glEnableClientState(GL_NORMAL_ARRAY); - glNormalPointer(GL_FLOAT, 0, mesh.normals.data()); - // Draw triangles - glDrawElements(GL_TRIANGLES, static_cast(mesh.indices.size()), GL_UNSIGNED_INT, mesh.indices.data()); + if (m_useVBOs) { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboPositions); + glVertexPointer(3, GL_FLOAT, 0, nullptr); + + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboNormals); + glNormalPointer(GL_FLOAT, 0, nullptr); + + if (appearance.textureId != NO_TEXTURE_ID) { + glBindBuffer(GL_ARRAY_BUFFER, mesh.vboTexcoords); + glTexCoordPointer(2, GL_FLOAT, 0, nullptr); + } + + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh.ibo); + glDrawElements(GL_TRIANGLES, static_cast(mesh.indices.size()), GL_UNSIGNED_INT, nullptr); + + glBindBuffer(GL_ARRAY_BUFFER, 0); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + } + else { + glVertexPointer(3, GL_FLOAT, 0, mesh.positions.data()); + glNormalPointer(GL_FLOAT, 0, mesh.normals.data()); + if (appearance.textureId != NO_TEXTURE_ID) { + glTexCoordPointer(2, GL_FLOAT, 0, mesh.texcoords.data()); + } + + glDrawElements(GL_TRIANGLES, static_cast(mesh.indices.size()), GL_UNSIGNED_INT, mesh.indices.data()); + } glPopMatrix(); } -HRESULT OpenGL15Renderer::FinalizeFrame() +HRESULT OpenGL1Renderer::FinalizeFrame() { glReadPixels(0, 0, m_width, m_height, GL_RGBA, GL_UNSIGNED_BYTE, m_renderedImage->pixels); glBindFramebuffer(GL_FRAMEBUFFER, 0); diff --git a/miniwin/src/d3drm/d3drm.cpp b/miniwin/src/d3drm/d3drm.cpp index e688d890..d6e67d39 100644 --- a/miniwin/src/d3drm/d3drm.cpp +++ b/miniwin/src/d3drm/d3drm.cpp @@ -7,8 +7,8 @@ #include "d3drmmesh_impl.h" #include "d3drmobject_impl.h" #include "d3drmrenderer.h" -#ifdef USE_OPENGL15 -#include "d3drmrenderer_opengl15.h" +#ifdef USE_OPENGL1 +#include "d3drmrenderer_opengl1.h" #endif #include "d3drmrenderer_sdl3gpu.h" #include "d3drmrenderer_software.h" @@ -144,9 +144,9 @@ HRESULT Direct3DRMImpl::CreateDeviceFromSurface( else if (SDL_memcmp(&guid, &SOFTWARE_GUID, sizeof(GUID)) == 0) { renderer = new Direct3DRMSoftwareRenderer(DDSDesc.dwWidth, DDSDesc.dwHeight); } -#ifdef USE_OPENGL15 - else if (SDL_memcmp(&guid, &OPENGL15_GUID, sizeof(GUID)) == 0) { - renderer = OpenGL15Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); +#ifdef USE_OPENGL1 + else if (SDL_memcmp(&guid, &OpenGL1_GUID, sizeof(GUID)) == 0) { + renderer = OpenGL1Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); } #endif else { diff --git a/miniwin/src/ddraw/ddraw.cpp b/miniwin/src/ddraw/ddraw.cpp index 9d817c9c..8ac249f1 100644 --- a/miniwin/src/ddraw/ddraw.cpp +++ b/miniwin/src/ddraw/ddraw.cpp @@ -1,5 +1,5 @@ -#ifdef USE_OPENGL15 -#include "d3drmrenderer_opengl15.h" +#ifdef USE_OPENGL1 +#include "d3drmrenderer_opengl1.h" #endif #include "d3drmrenderer_sdl3gpu.h" #include "d3drmrenderer_software.h" @@ -227,8 +227,8 @@ void EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx, Direct3DRMRenderer* devi HRESULT DirectDrawImpl::EnumDevices(LPD3DENUMDEVICESCALLBACK cb, void* ctx) { Direct3DRMSDL3GPU_EnumDevice(cb, ctx); -#ifdef USE_OPENGL15 - OpenGL15Renderer_EnumDevice(cb, ctx); +#ifdef USE_OPENGL1 + OpenGL1Renderer_EnumDevice(cb, ctx); #endif Direct3DRMSoftware_EnumDevice(cb, ctx); @@ -321,9 +321,9 @@ HRESULT DirectDrawImpl::CreateDevice( if (SDL_memcmp(&guid, &SDL3_GPU_GUID, sizeof(GUID)) == 0) { renderer = Direct3DRMSDL3GPURenderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); } -#ifdef USE_OPENGL15 - else if (SDL_memcmp(&guid, &OPENGL15_GUID, sizeof(GUID)) == 0) { - renderer = OpenGL15Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); +#ifdef USE_OPENGL1 + else if (SDL_memcmp(&guid, &OpenGL1_GUID, sizeof(GUID)) == 0) { + renderer = OpenGL1Renderer::Create(DDSDesc.dwWidth, DDSDesc.dwHeight); } #endif else if (SDL_memcmp(&guid, &SOFTWARE_GUID, sizeof(GUID)) == 0) { diff --git a/miniwin/src/internal/d3drmrenderer_opengl15.h b/miniwin/src/internal/d3drmrenderer_opengl1.h similarity index 74% rename from miniwin/src/internal/d3drmrenderer_opengl15.h rename to miniwin/src/internal/d3drmrenderer_opengl1.h index 8d88f4c0..98be09af 100644 --- a/miniwin/src/internal/d3drmrenderer_opengl15.h +++ b/miniwin/src/internal/d3drmrenderer_opengl1.h @@ -8,7 +8,7 @@ #include #include -DEFINE_GUID(OPENGL15_GUID, 0x682656F3, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03); +DEFINE_GUID(OpenGL1_GUID, 0x682656F3, 0x0000, 0x0000, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03); struct GLTextureCacheEntry { IDirect3DRMTexture* texture; @@ -20,17 +20,25 @@ struct GLMeshCacheEntry { const MeshGroup* meshGroup; int version; bool flat; + + // non-VBO cache std::vector positions; std::vector normals; std::vector texcoords; std::vector indices; + + // VBO cache + GLuint vboPositions; + GLuint vboNormals; + GLuint vboTexcoords; + GLuint ibo; }; -class OpenGL15Renderer : public Direct3DRMRenderer { +class OpenGL1Renderer : public Direct3DRMRenderer { public: static Direct3DRMRenderer* Create(DWORD width, DWORD height); - OpenGL15Renderer(int width, int height, SDL_GLContext context, GLuint fbo, GLuint colorTex, GLuint depthRb); - ~OpenGL15Renderer() override; + OpenGL1Renderer(int width, int height, SDL_GLContext context, GLuint fbo, GLuint colorTex, GLuint depthRb); + ~OpenGL1Renderer() override; void PushLights(const SceneLight* lightsArray, size_t count) override; void SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) override; Uint32 GetTextureId(IDirect3DRMTexture* texture) override; @@ -57,6 +65,7 @@ private: D3DRMMATRIX4D m_projection; SDL_Surface* m_renderedImage; int m_width, m_height; + bool m_useVBOs; std::vector m_lights; SDL_GLContext m_context; GLuint m_fbo = 0; @@ -64,11 +73,11 @@ private: GLuint m_depthRb = 0; }; -inline static void OpenGL15Renderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx) +inline static void OpenGL1Renderer_EnumDevice(LPD3DENUMDEVICESCALLBACK cb, void* ctx) { - Direct3DRMRenderer* device = OpenGL15Renderer::Create(640, 480); + Direct3DRMRenderer* device = OpenGL1Renderer::Create(640, 480); if (device) { - EnumDevice(cb, ctx, device, OPENGL15_GUID); + EnumDevice(cb, ctx, device, OpenGL1_GUID); delete device; } }