Upload mesh data to GPU using VBOs when available (#275)

This commit is contained in:
Anders Jenbo 2025-06-11 01:33:14 +02:00 committed by GitHub
parent 99c0d2d744
commit 2c9d9ba920
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 142 additions and 63 deletions

View File

@ -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

View File

@ -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 <cstring>
#include <vector>
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<Direct3DRMTextureImpl*>(iTexture);
auto surface = static_cast<DirectDrawSurfaceImpl*>(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<GLMeshDestroyContext*>(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<GLsizei>(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<GLsizei>(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<GLsizei>(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);

View File

@ -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 {

View File

@ -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) {

View File

@ -8,7 +8,7 @@
#include <SDL3/SDL.h>
#include <vector>
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<D3DVECTOR> positions;
std::vector<D3DVECTOR> normals;
std::vector<TexCoord> texcoords;
std::vector<DWORD> 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<SceneLight> 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;
}
}