diff --git a/engine/include/XCEngine/RHI/OpenGL/OpenGLSampler.h b/engine/include/XCEngine/RHI/OpenGL/OpenGLSampler.h index 9473c1e5..0e339dbd 100644 --- a/engine/include/XCEngine/RHI/OpenGL/OpenGLSampler.h +++ b/engine/include/XCEngine/RHI/OpenGL/OpenGLSampler.h @@ -35,7 +35,9 @@ struct OpenGLSamplerDesc { SamplerWrapMode wrapR = SamplerWrapMode::Repeat; SamplerCompareMode compareMode = SamplerCompareMode::None; int compareFunc = 0; + float mipLodBias = 0.0f; float maxAnisotropy = 1.0f; + float borderColor[4] = { 0.0f, 0.0f, 0.0f, 0.0f }; float minLod = -1000.0f; float maxLod = 1000.0f; }; diff --git a/engine/src/RHI/OpenGL/OpenGLCommandList.cpp b/engine/src/RHI/OpenGL/OpenGLCommandList.cpp index b73e172e..74868fb4 100644 --- a/engine/src/RHI/OpenGL/OpenGLCommandList.cpp +++ b/engine/src/RHI/OpenGL/OpenGLCommandList.cpp @@ -6,6 +6,7 @@ #include "XCEngine/RHI/OpenGL/OpenGLRenderPass.h" #include "XCEngine/RHI/OpenGL/OpenGLDescriptorSet.h" #include "XCEngine/RHI/OpenGL/OpenGLEnums.h" +#include #include namespace XCEngine { @@ -830,10 +831,17 @@ void OpenGLCommandList::CopyResource(RHIResourceView* dst, RHIResourceView* src) GLuint dstTex = dstView->GetTexture(); GLuint srcTex = srcView->GetTexture(); - if (dstTex && srcTex) { - glCopyImageSubData(srcTex, GL_TEXTURE_2D, 0, 0, 0, 0, - dstTex, GL_TEXTURE_2D, 0, 0, 0, 0, - 256, 256, 1); + const OpenGLTexture* dstTexture = dstView->GetTextureResource(); + const OpenGLTexture* srcTexture = srcView->GetTextureResource(); + if (dstTex && srcTex && dstTexture != nullptr && srcTexture != nullptr) { + const GLuint srcTarget = ToOpenGL(srcTexture->GetOpenGLType()); + const GLuint dstTarget = ToOpenGL(dstTexture->GetOpenGLType()); + const GLsizei copyWidth = static_cast(std::min(srcTexture->GetWidth(), dstTexture->GetWidth())); + const GLsizei copyHeight = static_cast(std::min(srcTexture->GetHeight(), dstTexture->GetHeight())); + const GLsizei copyDepth = static_cast(std::max(1u, std::min(srcTexture->GetDepth(), dstTexture->GetDepth()))); + glCopyImageSubData(srcTex, srcTarget, 0, 0, 0, 0, + dstTex, dstTarget, 0, 0, 0, 0, + copyWidth, copyHeight, copyDepth); } } diff --git a/engine/src/RHI/OpenGL/OpenGLDevice.cpp b/engine/src/RHI/OpenGL/OpenGLDevice.cpp index b1790191..7db3ad2c 100644 --- a/engine/src/RHI/OpenGL/OpenGLDevice.cpp +++ b/engine/src/RHI/OpenGL/OpenGLDevice.cpp @@ -9,6 +9,7 @@ #include "XCEngine/RHI/OpenGL/OpenGLTexture.h" #include "XCEngine/RHI/OpenGL/OpenGLShader.h" #include "XCEngine/RHI/OpenGL/OpenGLPipelineState.h" +#include "XCEngine/RHI/OpenGL/OpenGLEnums.h" #include "XCEngine/RHI/OpenGL/OpenGLFence.h" #include "XCEngine/RHI/OpenGL/OpenGLSampler.h" #include "XCEngine/RHI/OpenGL/OpenGLCommandList.h" @@ -121,6 +122,75 @@ FramebufferAttachmentType ResolveDepthAttachmentType(Format format) { : 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(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(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(desc.maxAnisotropy) : 1.0f; + break; + default: + break; + } + + glDesc.wrapS = ToOpenGLSamplerWrapMode(static_cast(desc.addressU)); + glDesc.wrapT = ToOpenGLSamplerWrapMode(static_cast(desc.addressV)); + glDesc.wrapR = ToOpenGLSamplerWrapMode(static_cast(desc.addressW)); + glDesc.compareFunc = static_cast(ToOpenGL(static_cast(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() { WNDCLASSEXW wc = {}; wc.cbSize = sizeof(WNDCLASSEXW); @@ -684,8 +754,11 @@ RHIFence* OpenGLDevice::CreateFence(const FenceDesc& desc) { RHISampler* OpenGLDevice::CreateSampler(const SamplerDesc& desc) { auto* sampler = new OpenGLSampler(); - OpenGLSamplerDesc samplerDesc = {}; - sampler->Initialize(samplerDesc); + const OpenGLSamplerDesc samplerDesc = ToOpenGLSamplerDesc(desc); + if (!sampler->Initialize(samplerDesc)) { + delete sampler; + return nullptr; + } return sampler; } diff --git a/engine/src/RHI/OpenGL/OpenGLSampler.cpp b/engine/src/RHI/OpenGL/OpenGLSampler.cpp index 7867fe87..229757c7 100644 --- a/engine/src/RHI/OpenGL/OpenGLSampler.cpp +++ b/engine/src/RHI/OpenGL/OpenGLSampler.cpp @@ -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_T, ToOpenGL(desc.wrapT)); 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_MAX_LOD, desc.maxLod); 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; } diff --git a/tests/RHI/unit/test_command_list.cpp b/tests/RHI/unit/test_command_list.cpp index 9ebf0d77..123e948e 100644 --- a/tests/RHI/unit/test_command_list.cpp +++ b/tests/RHI/unit/test_command_list.cpp @@ -9,6 +9,7 @@ #include "XCEngine/RHI/OpenGL/OpenGLDevice.h" #include "XCEngine/RHI/OpenGL/OpenGLResourceView.h" #include +#include using namespace XCEngine::RHI; @@ -538,6 +539,74 @@ TEST_P(RHITestFixture, CommandList_CopyResource_WithRealResources) { 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 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((y * kWidth + x) * 4); + srcPixels[index + 0] = static_cast(x * 3); + srcPixels[index + 1] = static_cast(y * 5); + srcPixels[index + 2] = static_cast((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(Format::R8G8B8A8_UNorm); + texDesc.textureType = static_cast(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(CommandQueueType::Direct); + RHICommandList* cmdList = GetDevice()->CreateCommandList(cmdDesc); + ASSERT_NE(cmdList, nullptr); + + cmdList->Reset(); + cmdList->CopyResource(dstView, srcView); + glFinish(); + cmdList->Close(); + + std::vector dstPixels(kWidth * kHeight * 4, 0); + const GLuint dstTextureId = static_cast(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) { AttachmentDesc colorDesc = {}; colorDesc.format = Format::R8G8B8A8_UNorm; diff --git a/tests/RHI/unit/test_sampler.cpp b/tests/RHI/unit/test_sampler.cpp index 75ebdf01..281f5c8b 100644 --- a/tests/RHI/unit/test_sampler.cpp +++ b/tests/RHI/unit/test_sampler.cpp @@ -1,5 +1,7 @@ #include "fixtures/RHITestFixture.h" #include "XCEngine/RHI/RHISampler.h" +#include "XCEngine/RHI/OpenGL/OpenGLSampler.h" +#include using namespace XCEngine::RHI; @@ -75,3 +77,51 @@ TEST_P(RHITestFixture, Sampler_GetNativeHandle) { sampler->Shutdown(); delete sampler; } + +TEST_P(RHITestFixture, Sampler_OpenGLHonorsSamplerDesc) { + if (GetBackendType() != RHIType::OpenGL) { + GTEST_SKIP() << "OpenGL-specific sampler parameter verification"; + } + + SamplerDesc desc = {}; + desc.filter = static_cast(FilterMode::ComparisonAnisotropic); + desc.addressU = static_cast(TextureAddressMode::Clamp); + desc.addressV = static_cast(TextureAddressMode::Mirror); + desc.addressW = static_cast(TextureAddressMode::Border); + desc.maxAnisotropy = 8; + desc.comparisonFunc = static_cast(ComparisonFunc::GreaterEqual); + desc.minLod = 1.0f; + desc.maxLod = 4.0f; + + RHISampler* sampler = GetDevice()->CreateSampler(desc); + ASSERT_NE(sampler, nullptr); + + const GLuint samplerId = static_cast(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; +}