Fix OpenGL sampler and copy semantics

This commit is contained in:
2026-03-26 02:14:21 +08:00
parent c47e871c5a
commit 1ef3048da1
6 changed files with 215 additions and 6 deletions

View File

@@ -35,7 +35,9 @@ struct OpenGLSamplerDesc {
SamplerWrapMode wrapR = SamplerWrapMode::Repeat; SamplerWrapMode wrapR = SamplerWrapMode::Repeat;
SamplerCompareMode compareMode = SamplerCompareMode::None; SamplerCompareMode compareMode = SamplerCompareMode::None;
int compareFunc = 0; int compareFunc = 0;
float mipLodBias = 0.0f;
float maxAnisotropy = 1.0f; float maxAnisotropy = 1.0f;
float borderColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f };
float minLod = -1000.0f; float minLod = -1000.0f;
float maxLod = 1000.0f; float maxLod = 1000.0f;
}; };

View File

@@ -6,6 +6,7 @@
#include "XCEngine/RHI/OpenGL/OpenGLRenderPass.h" #include "XCEngine/RHI/OpenGL/OpenGLRenderPass.h"
#include "XCEngine/RHI/OpenGL/OpenGLDescriptorSet.h" #include "XCEngine/RHI/OpenGL/OpenGLDescriptorSet.h"
#include "XCEngine/RHI/OpenGL/OpenGLEnums.h" #include "XCEngine/RHI/OpenGL/OpenGLEnums.h"
#include <algorithm>
#include <glad/glad.h> #include <glad/glad.h>
namespace XCEngine { namespace XCEngine {
@@ -830,10 +831,17 @@ void OpenGLCommandList::CopyResource(RHIResourceView* dst, RHIResourceView* src)
GLuint dstTex = dstView->GetTexture(); GLuint dstTex = dstView->GetTexture();
GLuint srcTex = srcView->GetTexture(); GLuint srcTex = srcView->GetTexture();
if (dstTex && srcTex) { const OpenGLTexture* dstTexture = dstView->GetTextureResource();
glCopyImageSubData(srcTex, GL_TEXTURE_2D, 0, 0, 0, 0, const OpenGLTexture* srcTexture = srcView->GetTextureResource();
dstTex, GL_TEXTURE_2D, 0, 0, 0, 0, if (dstTex && srcTex && dstTexture != nullptr && srcTexture != nullptr) {
256, 256, 1); const GLuint srcTarget = ToOpenGL(srcTexture->GetOpenGLType());
const GLuint dstTarget = ToOpenGL(dstTexture->GetOpenGLType());
const GLsizei copyWidth = static_cast<GLsizei>(std::min(srcTexture->GetWidth(), dstTexture->GetWidth()));
const GLsizei copyHeight = static_cast<GLsizei>(std::min(srcTexture->GetHeight(), dstTexture->GetHeight()));
const GLsizei copyDepth = static_cast<GLsizei>(std::max<uint32_t>(1u, std::min(srcTexture->GetDepth(), dstTexture->GetDepth())));
glCopyImageSubData(srcTex, srcTarget, 0, 0, 0, 0,
dstTex, dstTarget, 0, 0, 0, 0,
copyWidth, copyHeight, copyDepth);
} }
} }

View File

@@ -9,6 +9,7 @@
#include "XCEngine/RHI/OpenGL/OpenGLTexture.h" #include "XCEngine/RHI/OpenGL/OpenGLTexture.h"
#include "XCEngine/RHI/OpenGL/OpenGLShader.h" #include "XCEngine/RHI/OpenGL/OpenGLShader.h"
#include "XCEngine/RHI/OpenGL/OpenGLPipelineState.h" #include "XCEngine/RHI/OpenGL/OpenGLPipelineState.h"
#include "XCEngine/RHI/OpenGL/OpenGLEnums.h"
#include "XCEngine/RHI/OpenGL/OpenGLFence.h" #include "XCEngine/RHI/OpenGL/OpenGLFence.h"
#include "XCEngine/RHI/OpenGL/OpenGLSampler.h" #include "XCEngine/RHI/OpenGL/OpenGLSampler.h"
#include "XCEngine/RHI/OpenGL/OpenGLCommandList.h" #include "XCEngine/RHI/OpenGL/OpenGLCommandList.h"
@@ -121,6 +122,75 @@ FramebufferAttachmentType ResolveDepthAttachmentType(Format format) {
: FramebufferAttachmentType::Depth; : FramebufferAttachmentType::Depth;
} }
SamplerWrapMode ToOpenGLSamplerWrapMode(TextureAddressMode mode) {
switch (mode) {
case TextureAddressMode::Wrap:
return SamplerWrapMode::Repeat;
case TextureAddressMode::Mirror:
return SamplerWrapMode::MirroredRepeat;
case TextureAddressMode::Clamp:
return SamplerWrapMode::ClampToEdge;
case TextureAddressMode::Border:
return SamplerWrapMode::ClampToBorder;
case TextureAddressMode::MirrorOnce:
return SamplerWrapMode::ClampToEdge;
default:
return SamplerWrapMode::Repeat;
}
}
OpenGLSamplerDesc ToOpenGLSamplerDesc(const SamplerDesc& desc) {
OpenGLSamplerDesc glDesc = {};
const FilterMode filter = static_cast<FilterMode>(desc.filter);
switch (filter) {
case FilterMode::Point:
glDesc.minFilter = SamplerFilter::Nearest;
glDesc.magFilter = SamplerFilter::Nearest;
break;
case FilterMode::Linear:
glDesc.minFilter = SamplerFilter::Linear;
glDesc.magFilter = SamplerFilter::Linear;
break;
case FilterMode::Anisotropic:
glDesc.minFilter = SamplerFilter::LinearMipmapLinear;
glDesc.magFilter = SamplerFilter::Linear;
glDesc.maxAnisotropy = desc.maxAnisotropy > 0 ? static_cast<float>(desc.maxAnisotropy) : 1.0f;
break;
case FilterMode::ComparisonPoint:
glDesc.minFilter = SamplerFilter::Nearest;
glDesc.magFilter = SamplerFilter::Nearest;
glDesc.compareMode = SamplerCompareMode::CompareToRef;
break;
case FilterMode::ComparisonLinear:
glDesc.minFilter = SamplerFilter::Linear;
glDesc.magFilter = SamplerFilter::Linear;
glDesc.compareMode = SamplerCompareMode::CompareToRef;
break;
case FilterMode::ComparisonAnisotropic:
glDesc.minFilter = SamplerFilter::LinearMipmapLinear;
glDesc.magFilter = SamplerFilter::Linear;
glDesc.compareMode = SamplerCompareMode::CompareToRef;
glDesc.maxAnisotropy = desc.maxAnisotropy > 0 ? static_cast<float>(desc.maxAnisotropy) : 1.0f;
break;
default:
break;
}
glDesc.wrapS = ToOpenGLSamplerWrapMode(static_cast<TextureAddressMode>(desc.addressU));
glDesc.wrapT = ToOpenGLSamplerWrapMode(static_cast<TextureAddressMode>(desc.addressV));
glDesc.wrapR = ToOpenGLSamplerWrapMode(static_cast<TextureAddressMode>(desc.addressW));
glDesc.compareFunc = static_cast<int>(ToOpenGL(static_cast<ComparisonFunc>(desc.comparisonFunc)));
glDesc.mipLodBias = desc.mipLodBias;
glDesc.maxAnisotropy = glDesc.maxAnisotropy < 1.0f ? 1.0f : glDesc.maxAnisotropy;
glDesc.borderColor[0] = desc.borderColorR;
glDesc.borderColor[1] = desc.borderColorG;
glDesc.borderColor[2] = desc.borderColorB;
glDesc.borderColor[3] = desc.borderColorA;
glDesc.minLod = desc.minLod;
glDesc.maxLod = desc.maxLod;
return glDesc;
}
HWND CreateHiddenOpenGLWindow() { HWND CreateHiddenOpenGLWindow() {
WNDCLASSEXW wc = {}; WNDCLASSEXW wc = {};
wc.cbSize = sizeof(WNDCLASSEXW); wc.cbSize = sizeof(WNDCLASSEXW);
@@ -684,8 +754,11 @@ RHIFence* OpenGLDevice::CreateFence(const FenceDesc& desc) {
RHISampler* OpenGLDevice::CreateSampler(const SamplerDesc& desc) { RHISampler* OpenGLDevice::CreateSampler(const SamplerDesc& desc) {
auto* sampler = new OpenGLSampler(); auto* sampler = new OpenGLSampler();
OpenGLSamplerDesc samplerDesc = {}; const OpenGLSamplerDesc samplerDesc = ToOpenGLSamplerDesc(desc);
sampler->Initialize(samplerDesc); if (!sampler->Initialize(samplerDesc)) {
delete sampler;
return nullptr;
}
return sampler; return sampler;
} }

View File

@@ -20,9 +20,16 @@ bool OpenGLSampler::Initialize(const OpenGLSamplerDesc& desc) {
glSamplerParameteri(m_sampler, GL_TEXTURE_WRAP_S, ToOpenGL(desc.wrapS)); glSamplerParameteri(m_sampler, GL_TEXTURE_WRAP_S, ToOpenGL(desc.wrapS));
glSamplerParameteri(m_sampler, GL_TEXTURE_WRAP_T, ToOpenGL(desc.wrapT)); glSamplerParameteri(m_sampler, GL_TEXTURE_WRAP_T, ToOpenGL(desc.wrapT));
glSamplerParameteri(m_sampler, GL_TEXTURE_WRAP_R, ToOpenGL(desc.wrapR)); glSamplerParameteri(m_sampler, GL_TEXTURE_WRAP_R, ToOpenGL(desc.wrapR));
glSamplerParameterf(m_sampler, GL_TEXTURE_LOD_BIAS, desc.mipLodBias);
glSamplerParameterf(m_sampler, GL_TEXTURE_MIN_LOD, desc.minLod); glSamplerParameterf(m_sampler, GL_TEXTURE_MIN_LOD, desc.minLod);
glSamplerParameterf(m_sampler, GL_TEXTURE_MAX_LOD, desc.maxLod); glSamplerParameterf(m_sampler, GL_TEXTURE_MAX_LOD, desc.maxLod);
glSamplerParameterf(m_sampler, GL_TEXTURE_MAX_ANISOTROPY, desc.maxAnisotropy); glSamplerParameterf(m_sampler, GL_TEXTURE_MAX_ANISOTROPY, desc.maxAnisotropy);
glSamplerParameteri(
m_sampler,
GL_TEXTURE_COMPARE_MODE,
desc.compareMode == SamplerCompareMode::CompareToRef ? GL_COMPARE_REF_TO_TEXTURE : GL_NONE);
glSamplerParameteri(m_sampler, GL_TEXTURE_COMPARE_FUNC, desc.compareFunc);
glSamplerParameterfv(m_sampler, GL_TEXTURE_BORDER_COLOR, desc.borderColor);
return true; return true;
} }

View File

@@ -9,6 +9,7 @@
#include "XCEngine/RHI/OpenGL/OpenGLDevice.h" #include "XCEngine/RHI/OpenGL/OpenGLDevice.h"
#include "XCEngine/RHI/OpenGL/OpenGLResourceView.h" #include "XCEngine/RHI/OpenGL/OpenGLResourceView.h"
#include <glad/glad.h> #include <glad/glad.h>
#include <vector>
using namespace XCEngine::RHI; using namespace XCEngine::RHI;
@@ -538,6 +539,74 @@ TEST_P(RHITestFixture, CommandList_CopyResource_WithRealResources) {
delete dstTexture; delete dstTexture;
} }
TEST_P(RHITestFixture, CommandList_CopyResource_UsesActualTextureDimensionsOnOpenGL) {
if (GetBackendType() != RHIType::OpenGL) {
GTEST_SKIP() << "OpenGL-specific copy verification";
}
constexpr uint32_t kWidth = 64;
constexpr uint32_t kHeight = 32;
std::vector<uint8_t> srcPixels(kWidth * kHeight * 4);
for (uint32_t y = 0; y < kHeight; ++y) {
for (uint32_t x = 0; x < kWidth; ++x) {
const size_t index = static_cast<size_t>((y * kWidth + x) * 4);
srcPixels[index + 0] = static_cast<uint8_t>(x * 3);
srcPixels[index + 1] = static_cast<uint8_t>(y * 5);
srcPixels[index + 2] = static_cast<uint8_t>((x + y) * 7);
srcPixels[index + 3] = 255;
}
}
TextureDesc texDesc = {};
texDesc.width = kWidth;
texDesc.height = kHeight;
texDesc.depth = 1;
texDesc.mipLevels = 1;
texDesc.arraySize = 1;
texDesc.format = static_cast<uint32_t>(Format::R8G8B8A8_UNorm);
texDesc.textureType = static_cast<uint32_t>(TextureType::Texture2D);
texDesc.sampleCount = 1;
texDesc.sampleQuality = 0;
RHITexture* srcTexture = GetDevice()->CreateTexture(texDesc, srcPixels.data(), srcPixels.size(), kWidth * 4);
ASSERT_NE(srcTexture, nullptr);
RHITexture* dstTexture = GetDevice()->CreateTexture(texDesc);
ASSERT_NE(dstTexture, nullptr);
RHIResourceView* srcView = GetDevice()->CreateShaderResourceView(srcTexture, {});
RHIResourceView* dstView = GetDevice()->CreateShaderResourceView(dstTexture, {});
ASSERT_NE(srcView, nullptr);
ASSERT_NE(dstView, nullptr);
CommandListDesc cmdDesc = {};
cmdDesc.commandListType = static_cast<uint32_t>(CommandQueueType::Direct);
RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc);
ASSERT_NE(cmdList, nullptr);
cmdList->Reset();
cmdList->CopyResource(dstView, srcView);
glFinish();
cmdList->Close();
std::vector<uint8_t> dstPixels(kWidth * kHeight * 4, 0);
const GLuint dstTextureId = static_cast<OpenGLResourceView*>(dstView)->GetTexture();
glBindTexture(GL_TEXTURE_2D, dstTextureId);
glGetTexImage(GL_TEXTURE_2D, 0, GL_RGBA, GL_UNSIGNED_BYTE, dstPixels.data());
glBindTexture(GL_TEXTURE_2D, 0);
EXPECT_EQ(dstPixels, srcPixels);
cmdList->Shutdown();
delete cmdList;
delete srcView;
delete dstView;
srcTexture->Shutdown();
delete srcTexture;
dstTexture->Shutdown();
delete dstTexture;
}
TEST_P(RHITestFixture, CommandList_BeginEndRenderPass_Basic) { TEST_P(RHITestFixture, CommandList_BeginEndRenderPass_Basic) {
AttachmentDesc colorDesc = {}; AttachmentDesc colorDesc = {};
colorDesc.format = Format::R8G8B8A8_UNorm; colorDesc.format = Format::R8G8B8A8_UNorm;

View File

@@ -1,5 +1,7 @@
#include "fixtures/RHITestFixture.h" #include "fixtures/RHITestFixture.h"
#include "XCEngine/RHI/RHISampler.h" #include "XCEngine/RHI/RHISampler.h"
#include "XCEngine/RHI/OpenGL/OpenGLSampler.h"
#include <glad/glad.h>
using namespace XCEngine::RHI; using namespace XCEngine::RHI;
@@ -75,3 +77,51 @@ TEST_P(RHITestFixture, Sampler_GetNativeHandle) {
sampler->Shutdown(); sampler->Shutdown();
delete sampler; delete sampler;
} }
TEST_P(RHITestFixture, Sampler_OpenGLHonorsSamplerDesc) {
if (GetBackendType() != RHIType::OpenGL) {
GTEST_SKIP() << "OpenGL-specific sampler parameter verification";
}
SamplerDesc desc = {};
desc.filter = static_cast<uint32_t>(FilterMode::ComparisonAnisotropic);
desc.addressU = static_cast<uint32_t>(TextureAddressMode::Clamp);
desc.addressV = static_cast<uint32_t>(TextureAddressMode::Mirror);
desc.addressW = static_cast<uint32_t>(TextureAddressMode::Border);
desc.maxAnisotropy = 8;
desc.comparisonFunc = static_cast<uint32_t>(ComparisonFunc::GreaterEqual);
desc.minLod = 1.0f;
desc.maxLod = 4.0f;
RHISampler* sampler = GetDevice()->CreateSampler(desc);
ASSERT_NE(sampler, nullptr);
const GLuint samplerId = static_cast<OpenGLSampler*>(sampler)->GetID();
GLint value = 0;
glGetSamplerParameteriv(samplerId, GL_TEXTURE_WRAP_S, &value);
EXPECT_EQ(value, GL_CLAMP_TO_EDGE);
glGetSamplerParameteriv(samplerId, GL_TEXTURE_WRAP_T, &value);
EXPECT_EQ(value, GL_MIRRORED_REPEAT);
glGetSamplerParameteriv(samplerId, GL_TEXTURE_WRAP_R, &value);
EXPECT_EQ(value, GL_CLAMP_TO_BORDER);
glGetSamplerParameteriv(samplerId, GL_TEXTURE_MIN_FILTER, &value);
EXPECT_EQ(value, GL_LINEAR_MIPMAP_LINEAR);
glGetSamplerParameteriv(samplerId, GL_TEXTURE_MAG_FILTER, &value);
EXPECT_EQ(value, GL_LINEAR);
glGetSamplerParameteriv(samplerId, GL_TEXTURE_COMPARE_MODE, &value);
EXPECT_EQ(value, GL_COMPARE_REF_TO_TEXTURE);
glGetSamplerParameteriv(samplerId, GL_TEXTURE_COMPARE_FUNC, &value);
EXPECT_EQ(value, GL_GEQUAL);
GLfloat floatValue = 0.0f;
glGetSamplerParameterfv(samplerId, GL_TEXTURE_MAX_ANISOTROPY, &floatValue);
EXPECT_GE(floatValue, 8.0f);
glGetSamplerParameterfv(samplerId, GL_TEXTURE_MIN_LOD, &floatValue);
EXPECT_FLOAT_EQ(floatValue, 1.0f);
glGetSamplerParameterfv(samplerId, GL_TEXTURE_MAX_LOD, &floatValue);
EXPECT_FLOAT_EQ(floatValue, 4.0f);
sampler->Shutdown();
delete sampler;
}