Formalize cubemap skybox pipeline across backends
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
// XC_BUILTIN_SKYBOX_OPENGL_PS
|
||||
#version 430
|
||||
|
||||
layout(binding = 0) uniform sampler2D uSkyboxTexture;
|
||||
layout(binding = 0) uniform samplerCube uSkyboxTexture;
|
||||
|
||||
layout(std140, binding = 0) uniform EnvironmentConstants {
|
||||
vec4 gSkyboxTopColor;
|
||||
@@ -37,18 +37,14 @@ vec3 EvaluateProceduralSkybox(vec3 viewRay) {
|
||||
return color;
|
||||
}
|
||||
|
||||
vec2 ComputePanoramicUv(vec3 viewRay) {
|
||||
vec3 RotateAroundY(vec3 viewRay) {
|
||||
float rotation = gSkyboxRotationAndMode.x;
|
||||
float sinTheta = sin(rotation);
|
||||
float cosTheta = cos(rotation);
|
||||
vec3 rotatedRay = normalize(vec3(
|
||||
return normalize(vec3(
|
||||
viewRay.x * cosTheta - viewRay.z * sinTheta,
|
||||
viewRay.y,
|
||||
viewRay.x * sinTheta + viewRay.z * cosTheta));
|
||||
|
||||
float u = fract(atan(rotatedRay.z, rotatedRay.x) * XC_INV_TWO_PI + 0.5);
|
||||
float v = acos(clamp(rotatedRay.y, -1.0, 1.0)) * XC_INV_PI;
|
||||
return vec2(u, clamp(v, 0.0, 1.0));
|
||||
}
|
||||
|
||||
void main() {
|
||||
@@ -62,8 +58,7 @@ void main() {
|
||||
|
||||
vec3 color = EvaluateProceduralSkybox(viewRay);
|
||||
if (gSkyboxRotationAndMode.y > 0.5) {
|
||||
vec2 uv = ComputePanoramicUv(viewRay);
|
||||
color = texture(uSkyboxTexture, uv).rgb *
|
||||
color = texture(uSkyboxTexture, RotateAroundY(viewRay)).rgb *
|
||||
gSkyboxTintAndExposure.rgb *
|
||||
gSkyboxTintAndExposure.w;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@ layout(set = 1, binding = 0, std140) uniform MaterialConstants {
|
||||
vec4 gSkyboxRotationAndMode;
|
||||
};
|
||||
|
||||
layout(set = 2, binding = 0) uniform texture2D uSkyboxTexture;
|
||||
layout(set = 2, binding = 0) uniform textureCube uSkyboxTexture;
|
||||
layout(set = 3, binding = 0) uniform sampler uLinearSampler;
|
||||
|
||||
layout(location = 0) in vec2 vNdc;
|
||||
@@ -38,18 +38,14 @@ vec3 EvaluateProceduralSkybox(vec3 viewRay) {
|
||||
return color;
|
||||
}
|
||||
|
||||
vec2 ComputePanoramicUv(vec3 viewRay) {
|
||||
vec3 RotateAroundY(vec3 viewRay) {
|
||||
float rotation = gSkyboxRotationAndMode.x;
|
||||
float sinTheta = sin(rotation);
|
||||
float cosTheta = cos(rotation);
|
||||
vec3 rotatedRay = normalize(vec3(
|
||||
return normalize(vec3(
|
||||
viewRay.x * cosTheta - viewRay.z * sinTheta,
|
||||
viewRay.y,
|
||||
viewRay.x * sinTheta + viewRay.z * cosTheta));
|
||||
|
||||
float u = fract(atan(rotatedRay.z, rotatedRay.x) * XC_INV_TWO_PI + 0.5);
|
||||
float v = acos(clamp(rotatedRay.y, -1.0, 1.0)) * XC_INV_PI;
|
||||
return vec2(u, clamp(v, 0.0, 1.0));
|
||||
}
|
||||
|
||||
void main() {
|
||||
@@ -63,8 +59,7 @@ void main() {
|
||||
|
||||
vec3 color = EvaluateProceduralSkybox(viewRay);
|
||||
if (gSkyboxRotationAndMode.y > 0.5) {
|
||||
vec2 uv = ComputePanoramicUv(viewRay);
|
||||
color = texture(sampler2D(uSkyboxTexture, uLinearSampler), uv).rgb *
|
||||
color = texture(samplerCube(uSkyboxTexture, uLinearSampler), RotateAroundY(viewRay)).rgb *
|
||||
gSkyboxTintAndExposure.rgb *
|
||||
gSkyboxTintAndExposure.w;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
// XC_BUILTIN_SKYBOX_D3D12_PS
|
||||
Texture2D gSkyboxTexture : register(t0);
|
||||
TextureCube gSkyboxTexture : register(t0);
|
||||
SamplerState gLinearSampler : register(s0);
|
||||
|
||||
cbuffer EnvironmentConstants : register(b0) {
|
||||
@@ -37,18 +37,14 @@ float3 EvaluateProceduralSkybox(float3 viewRay) {
|
||||
return color;
|
||||
}
|
||||
|
||||
float2 ComputePanoramicUv(float3 viewRay) {
|
||||
float3 RotateAroundY(float3 viewRay) {
|
||||
const float rotation = gSkyboxRotationAndMode.x;
|
||||
const float sinTheta = sin(rotation);
|
||||
const float cosTheta = cos(rotation);
|
||||
const float3 rotatedRay = normalize(float3(
|
||||
return normalize(float3(
|
||||
viewRay.x * cosTheta - viewRay.z * sinTheta,
|
||||
viewRay.y,
|
||||
viewRay.x * sinTheta + viewRay.z * cosTheta));
|
||||
|
||||
const float u = frac(atan2(rotatedRay.z, rotatedRay.x) * XC_INV_TWO_PI + 0.5f);
|
||||
const float v = acos(clamp(rotatedRay.y, -1.0f, 1.0f)) * XC_INV_PI;
|
||||
return float2(u, saturate(v));
|
||||
}
|
||||
|
||||
float4 MainPS(PSInput input) : SV_Target {
|
||||
@@ -62,8 +58,7 @@ float4 MainPS(PSInput input) : SV_Target {
|
||||
|
||||
float3 color = EvaluateProceduralSkybox(viewRay);
|
||||
if (gSkyboxRotationAndMode.y > 0.5f) {
|
||||
const float2 uv = ComputePanoramicUv(viewRay);
|
||||
color = gSkyboxTexture.Sample(gLinearSampler, uv).rgb *
|
||||
color = gSkyboxTexture.Sample(gLinearSampler, RotateAroundY(viewRay)).rgb *
|
||||
gSkyboxTintAndExposure.rgb *
|
||||
gSkyboxTintAndExposure.w;
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@ Shader "Builtin Skybox"
|
||||
_Tint ("Tint", Color) = (1,1,1,1) [Semantic(Tint)]
|
||||
_Exposure ("Exposure", Float) = 1.0 [Semantic(Exposure)]
|
||||
_Rotation ("Rotation", Float) = 0.0 [Semantic(Rotation)]
|
||||
_MainTex ("Panoramic", 2D) = "white" [Semantic(SkyboxTexture)]
|
||||
_Tex ("Cubemap (HDR)", Cube) = "white" [Semantic(SkyboxTexture)]
|
||||
}
|
||||
SubShader
|
||||
{
|
||||
@@ -17,7 +17,7 @@ Shader "Builtin Skybox"
|
||||
{
|
||||
EnvironmentConstants (ConstantBuffer, 0, 0) [Semantic(Environment)]
|
||||
MaterialConstants (ConstantBuffer, 1, 0) [Semantic(Material)]
|
||||
SkyboxTexture (Texture2D, 2, 0) [Semantic(SkyboxTexture)]
|
||||
SkyboxTexture (TextureCube, 2, 0) [Semantic(SkyboxTexture)]
|
||||
LinearClampSampler (Sampler, 3, 0) [Semantic(LinearClampSampler)]
|
||||
}
|
||||
HLSLPROGRAM
|
||||
|
||||
@@ -19,9 +19,9 @@ public:
|
||||
|
||||
bool Initialize(ID3D12Device* device, const D3D12_RESOURCE_DESC& desc, D3D12_RESOURCE_STATES initialState = D3D12_RESOURCE_STATE_COMMON);
|
||||
bool InitializeFromExisting(ID3D12Resource* resource, bool ownsResource = false);
|
||||
bool InitializeFromData(ID3D12Device* device, ID3D12GraphicsCommandList* commandList,
|
||||
const void* pixelData, uint32_t width, uint32_t height, DXGI_FORMAT format, uint32_t rowPitch = 0,
|
||||
ComPtr<ID3D12Resource>* uploadBuffer = nullptr);
|
||||
bool InitializeFromData(ID3D12Device* device, ID3D12GraphicsCommandList* commandList,
|
||||
const D3D12_RESOURCE_DESC& textureDesc, TextureType textureType, const void* pixelData, size_t pixelDataSize,
|
||||
uint32_t rowPitch = 0, ComPtr<ID3D12Resource>* uploadBuffer = nullptr);
|
||||
bool InitializeDepthStencil(ID3D12Device* device, uint32_t width, uint32_t height, DXGI_FORMAT format = DXGI_FORMAT_D24_UNORM_S8_UINT);
|
||||
void Shutdown() override;
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ struct DescriptorBinding {
|
||||
uint32_t count;
|
||||
std::vector<uint32_t> textureUnits;
|
||||
std::vector<uint32_t> textureIds;
|
||||
std::vector<uint32_t> textureTargets;
|
||||
std::vector<uint32_t> samplerIds;
|
||||
};
|
||||
|
||||
|
||||
@@ -147,16 +147,21 @@ inline const Resources::Texture* ResolveSkyboxTexture(const Resources::Material*
|
||||
|
||||
if (const Resources::ShaderPropertyDesc* property = FindShaderPropertyBySemantic(material, "SkyboxTexture")) {
|
||||
const Resources::ResourceHandle<Resources::Texture> textureHandle = material->GetTexture(property->name);
|
||||
if (textureHandle.Get() != nullptr && textureHandle->IsValid()) {
|
||||
if (textureHandle.Get() != nullptr &&
|
||||
textureHandle->IsValid() &&
|
||||
(textureHandle->GetTextureType() == Resources::TextureType::TextureCube ||
|
||||
textureHandle->GetTextureType() == Resources::TextureType::TextureCubeArray)) {
|
||||
return textureHandle.Get();
|
||||
}
|
||||
}
|
||||
|
||||
static const char* kSkyboxTexturePropertyNames[] = {
|
||||
"_Tex",
|
||||
"_MainTex",
|
||||
"_PanoramicTex",
|
||||
"_Cube",
|
||||
"_SkyboxTexture",
|
||||
"panoramicTexture",
|
||||
"cubemap",
|
||||
"skyboxCubemap",
|
||||
"skyboxTexture",
|
||||
"texture"
|
||||
};
|
||||
@@ -164,7 +169,10 @@ inline const Resources::Texture* ResolveSkyboxTexture(const Resources::Material*
|
||||
for (const char* propertyName : kSkyboxTexturePropertyNames) {
|
||||
const Resources::ResourceHandle<Resources::Texture> textureHandle =
|
||||
material->GetTexture(Containers::String(propertyName));
|
||||
if (textureHandle.Get() != nullptr && textureHandle->IsValid()) {
|
||||
if (textureHandle.Get() != nullptr &&
|
||||
textureHandle->IsValid() &&
|
||||
(textureHandle->GetTextureType() == Resources::TextureType::TextureCube ||
|
||||
textureHandle->GetTextureType() == Resources::TextureType::TextureCubeArray)) {
|
||||
return textureHandle.Get();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -285,8 +285,10 @@ private:
|
||||
std::unordered_map<DynamicDescriptorSetKey, CachedDescriptorSet, DynamicDescriptorSetKeyHash> m_dynamicDescriptorSets;
|
||||
RHI::RHISampler* m_sampler = nullptr;
|
||||
RHI::RHISampler* m_shadowSampler = nullptr;
|
||||
RHI::RHITexture* m_fallbackTexture = nullptr;
|
||||
RHI::RHIResourceView* m_fallbackTextureView = nullptr;
|
||||
RHI::RHITexture* m_fallbackTexture2D = nullptr;
|
||||
RHI::RHIResourceView* m_fallbackTexture2DView = nullptr;
|
||||
RHI::RHITexture* m_fallbackTextureCube = nullptr;
|
||||
RHI::RHIResourceView* m_fallbackTextureCubeView = nullptr;
|
||||
RHI::RHIPipelineLayout* m_skyboxPipelineLayout = nullptr;
|
||||
RHI::RHIPipelineState* m_skyboxPipelineState = nullptr;
|
||||
OwnedDescriptorSet m_skyboxEnvironmentSet = {};
|
||||
|
||||
@@ -53,7 +53,7 @@ public:
|
||||
|
||||
bool Create(Core::uint32 width, Core::uint32 height, Core::uint32 depth,
|
||||
Core::uint32 mipLevels, TextureType type, TextureFormat format,
|
||||
const void* data, size_t dataSize);
|
||||
const void* data, size_t dataSize, Core::uint32 arraySize = 1);
|
||||
bool GenerateMipmaps();
|
||||
|
||||
private:
|
||||
|
||||
@@ -196,6 +196,46 @@ D3D12_RTV_DIMENSION ResolveRTVDimension(const ResourceViewDesc& desc, TextureTyp
|
||||
}
|
||||
}
|
||||
|
||||
D3D12_SRV_DIMENSION ResolveSRVDimension(const ResourceViewDesc& desc, TextureType textureType) {
|
||||
switch (desc.dimension) {
|
||||
case ResourceViewDimension::Texture1D:
|
||||
return D3D12_SRV_DIMENSION_TEXTURE1D;
|
||||
case ResourceViewDimension::Texture1DArray:
|
||||
return D3D12_SRV_DIMENSION_TEXTURE1DARRAY;
|
||||
case ResourceViewDimension::Texture2DArray:
|
||||
return D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
|
||||
case ResourceViewDimension::Texture3D:
|
||||
return D3D12_SRV_DIMENSION_TEXTURE3D;
|
||||
case ResourceViewDimension::TextureCube:
|
||||
return D3D12_SRV_DIMENSION_TEXTURECUBE;
|
||||
case ResourceViewDimension::TextureCubeArray:
|
||||
return D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
|
||||
case ResourceViewDimension::Texture2DMS:
|
||||
return D3D12_SRV_DIMENSION_TEXTURE2DMS;
|
||||
case ResourceViewDimension::Texture2DMSArray:
|
||||
return D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY;
|
||||
case ResourceViewDimension::Unknown:
|
||||
break;
|
||||
default:
|
||||
return D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
}
|
||||
|
||||
switch (textureType) {
|
||||
case TextureType::Texture1D:
|
||||
return D3D12_SRV_DIMENSION_TEXTURE1D;
|
||||
case TextureType::Texture2DArray:
|
||||
return D3D12_SRV_DIMENSION_TEXTURE2DARRAY;
|
||||
case TextureType::Texture3D:
|
||||
return D3D12_SRV_DIMENSION_TEXTURE3D;
|
||||
case TextureType::TextureCube:
|
||||
return D3D12_SRV_DIMENSION_TEXTURECUBE;
|
||||
case TextureType::TextureCubeArray:
|
||||
return D3D12_SRV_DIMENSION_TEXTURECUBEARRAY;
|
||||
default:
|
||||
return D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
}
|
||||
}
|
||||
|
||||
bool IsDepthFormat(Format format) {
|
||||
return format == Format::D16_UNorm ||
|
||||
format == Format::D24_UNorm_S8_UInt ||
|
||||
@@ -680,10 +720,6 @@ RHITexture* D3D12Device::CreateTexture(const TextureDesc& desc, const void* init
|
||||
return CreateTexture(desc);
|
||||
}
|
||||
|
||||
if (desc.textureType != static_cast<uint32_t>(TextureType::Texture2D)) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const Format format = static_cast<Format>(desc.format);
|
||||
uint32_t resolvedRowPitch = rowPitch;
|
||||
if (resolvedRowPitch == 0) {
|
||||
@@ -716,14 +752,27 @@ RHITexture* D3D12Device::CreateTexture(const TextureDesc& desc, const void* init
|
||||
uploadCommandList.Reset();
|
||||
|
||||
auto* texture = new D3D12Texture();
|
||||
D3D12_RESOURCE_DESC textureDesc = {};
|
||||
const TextureType textureType = ResolveTextureType(desc.textureType);
|
||||
textureDesc.Dimension = ToD3D12(textureType);
|
||||
textureDesc.Alignment = 0;
|
||||
textureDesc.Width = desc.width;
|
||||
textureDesc.Height = desc.height;
|
||||
textureDesc.DepthOrArraySize = ResolveDepthOrArraySize(desc, textureType);
|
||||
textureDesc.MipLevels = desc.mipLevels > 0 ? static_cast<uint16_t>(desc.mipLevels) : 1;
|
||||
textureDesc.Format = ResolveD3D12ResourceFormat(format);
|
||||
textureDesc.SampleDesc.Count = desc.sampleCount > 0 ? desc.sampleCount : 1;
|
||||
textureDesc.SampleDesc.Quality = desc.sampleQuality;
|
||||
textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
||||
textureDesc.Flags = static_cast<D3D12_RESOURCE_FLAGS>(desc.flags);
|
||||
ComPtr<ID3D12Resource> uploadBuffer;
|
||||
if (!texture->InitializeFromData(
|
||||
m_device.Get(),
|
||||
uploadCommandList.GetCommandList(),
|
||||
textureDesc,
|
||||
textureType,
|
||||
initialData,
|
||||
desc.width,
|
||||
desc.height,
|
||||
ToD3D12(format),
|
||||
initialDataSize,
|
||||
resolvedRowPitch,
|
||||
&uploadBuffer)) {
|
||||
delete texture;
|
||||
@@ -734,6 +783,8 @@ RHITexture* D3D12Device::CreateTexture(const TextureDesc& desc, const void* init
|
||||
}
|
||||
|
||||
texture->SetState(ResourceStates::PixelShaderResource);
|
||||
texture->SetFormat(format);
|
||||
texture->SetTextureType(textureType);
|
||||
|
||||
uploadCommandList.Close();
|
||||
ID3D12CommandList* commandLists[] = { uploadCommandList.GetCommandList() };
|
||||
@@ -1074,11 +1125,52 @@ RHIResourceView* D3D12Device::CreateShaderResourceView(RHITexture* texture, cons
|
||||
|
||||
D3D12_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
|
||||
const Format format = desc.format != 0 ? static_cast<Format>(desc.format) : texture->GetFormat();
|
||||
const TextureType textureType = texture->GetTextureType();
|
||||
srvDesc.Format = ResolveD3D12ShaderResourceViewFormat(format);
|
||||
srvDesc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D;
|
||||
srvDesc.Texture2D.MostDetailedMip = desc.mipLevel;
|
||||
srvDesc.Texture2D.MipLevels = 1;
|
||||
srvDesc.ViewDimension = ResolveSRVDimension(desc, textureType);
|
||||
srvDesc.Shader4ComponentMapping = D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING;
|
||||
switch (srvDesc.ViewDimension) {
|
||||
case D3D12_SRV_DIMENSION_TEXTURE1D:
|
||||
srvDesc.Texture1D.MostDetailedMip = desc.mipLevel;
|
||||
srvDesc.Texture1D.MipLevels = 1;
|
||||
break;
|
||||
case D3D12_SRV_DIMENSION_TEXTURE1DARRAY:
|
||||
srvDesc.Texture1DArray.MostDetailedMip = desc.mipLevel;
|
||||
srvDesc.Texture1DArray.MipLevels = 1;
|
||||
srvDesc.Texture1DArray.FirstArraySlice = desc.firstArraySlice;
|
||||
srvDesc.Texture1DArray.ArraySize = desc.arraySize > 0 ? desc.arraySize : 1;
|
||||
break;
|
||||
case D3D12_SRV_DIMENSION_TEXTURE2DARRAY:
|
||||
srvDesc.Texture2DArray.MostDetailedMip = desc.mipLevel;
|
||||
srvDesc.Texture2DArray.MipLevels = 1;
|
||||
srvDesc.Texture2DArray.FirstArraySlice = desc.firstArraySlice;
|
||||
srvDesc.Texture2DArray.ArraySize = desc.arraySize > 0 ? desc.arraySize : 1;
|
||||
srvDesc.Texture2DArray.PlaneSlice = desc.planeSlice;
|
||||
break;
|
||||
case D3D12_SRV_DIMENSION_TEXTURE3D:
|
||||
srvDesc.Texture3D.MostDetailedMip = desc.mipLevel;
|
||||
srvDesc.Texture3D.MipLevels = 1;
|
||||
break;
|
||||
case D3D12_SRV_DIMENSION_TEXTURECUBE:
|
||||
srvDesc.TextureCube.MostDetailedMip = desc.mipLevel;
|
||||
srvDesc.TextureCube.MipLevels = 1;
|
||||
break;
|
||||
case D3D12_SRV_DIMENSION_TEXTURECUBEARRAY:
|
||||
srvDesc.TextureCubeArray.MostDetailedMip = desc.mipLevel;
|
||||
srvDesc.TextureCubeArray.MipLevels = 1;
|
||||
srvDesc.TextureCubeArray.First2DArrayFace = desc.firstArraySlice;
|
||||
srvDesc.TextureCubeArray.NumCubes = desc.arraySize > 0 ? std::max<uint32_t>(desc.arraySize / 6u, 1u) : 1u;
|
||||
break;
|
||||
case D3D12_SRV_DIMENSION_TEXTURE2DMS:
|
||||
case D3D12_SRV_DIMENSION_TEXTURE2DMSARRAY:
|
||||
break;
|
||||
case D3D12_SRV_DIMENSION_TEXTURE2D:
|
||||
default:
|
||||
srvDesc.Texture2D.MostDetailedMip = desc.mipLevel;
|
||||
srvDesc.Texture2D.MipLevels = 1;
|
||||
srvDesc.Texture2D.PlaneSlice = desc.planeSlice;
|
||||
break;
|
||||
}
|
||||
|
||||
auto heap = std::make_unique<D3D12DescriptorHeap>();
|
||||
if (!heap->Initialize(m_device.Get(), DescriptorHeapType::CBV_SRV_UAV, 1, false)) {
|
||||
@@ -1087,6 +1179,9 @@ RHIResourceView* D3D12Device::CreateShaderResourceView(RHITexture* texture, cons
|
||||
}
|
||||
|
||||
view->InitializeAsShaderResource(m_device.Get(), resource, &srvDesc, heap.get(), 0);
|
||||
if (IsDepthFormat(format)) {
|
||||
view->SetFormat(format);
|
||||
}
|
||||
view->SetOwnedHeap(std::move(heap));
|
||||
|
||||
return view;
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
#include "XCEngine/RHI/D3D12/D3D12Texture.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
|
||||
@@ -72,22 +75,12 @@ bool D3D12Texture::InitializeFromExisting(ID3D12Resource* resource, bool ownsRes
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D12Texture::InitializeFromData(ID3D12Device* device, ID3D12GraphicsCommandList* commandList,
|
||||
const void* pixelData, uint32_t width, uint32_t height, DXGI_FORMAT format, uint32_t rowPitch,
|
||||
ComPtr<ID3D12Resource>* uploadBuffer) {
|
||||
|
||||
D3D12_RESOURCE_DESC textureDesc = {};
|
||||
textureDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;
|
||||
textureDesc.Alignment = 0;
|
||||
textureDesc.Width = width;
|
||||
textureDesc.Height = height;
|
||||
textureDesc.DepthOrArraySize = 1;
|
||||
textureDesc.MipLevels = 1;
|
||||
textureDesc.Format = format;
|
||||
textureDesc.SampleDesc.Count = 1;
|
||||
textureDesc.SampleDesc.Quality = 0;
|
||||
textureDesc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN;
|
||||
textureDesc.Flags = D3D12_RESOURCE_FLAG_NONE;
|
||||
bool D3D12Texture::InitializeFromData(ID3D12Device* device, ID3D12GraphicsCommandList* commandList,
|
||||
const D3D12_RESOURCE_DESC& textureDesc, TextureType textureType, const void* pixelData, size_t pixelDataSize,
|
||||
uint32_t rowPitch, ComPtr<ID3D12Resource>* uploadBuffer) {
|
||||
if (device == nullptr || commandList == nullptr || pixelData == nullptr || pixelDataSize == 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
D3D12_HEAP_PROPERTIES heapProperties = {};
|
||||
heapProperties.Type = D3D12_HEAP_TYPE_DEFAULT;
|
||||
@@ -105,16 +98,24 @@ bool D3D12Texture::InitializeFromData(ID3D12Device* device, ID3D12GraphicsComman
|
||||
return false;
|
||||
}
|
||||
m_ownsResource = true;
|
||||
m_format = FromD3D12(format);
|
||||
m_textureType = TextureType::Texture2D;
|
||||
m_format = FromD3D12(textureDesc.Format);
|
||||
m_textureType = textureType;
|
||||
|
||||
textureDesc = m_resource->GetDesc();
|
||||
const UINT subresourceCount = textureDesc.MipLevels *
|
||||
(textureDesc.Dimension == D3D12_RESOURCE_DIMENSION_TEXTURE3D ? 1u : textureDesc.DepthOrArraySize);
|
||||
std::vector<D3D12_PLACED_SUBRESOURCE_FOOTPRINT> subresourceFootprints(subresourceCount);
|
||||
std::vector<UINT> subresourceRowCounts(subresourceCount);
|
||||
std::vector<UINT64> subresourceRowSizes(subresourceCount);
|
||||
UINT64 memorySizeUsed = 0;
|
||||
UINT64 rowSizeInBytes = 0;
|
||||
UINT rowUsed = 0;
|
||||
D3D12_PLACED_SUBRESOURCE_FOOTPRINT subresourceFootprint;
|
||||
device->GetCopyableFootprints(&textureDesc, 0, 1, 0,
|
||||
&subresourceFootprint, &rowUsed, &rowSizeInBytes, &memorySizeUsed);
|
||||
device->GetCopyableFootprints(
|
||||
&textureDesc,
|
||||
0,
|
||||
subresourceCount,
|
||||
0,
|
||||
subresourceFootprints.data(),
|
||||
subresourceRowCounts.data(),
|
||||
subresourceRowSizes.data(),
|
||||
&memorySizeUsed);
|
||||
|
||||
ComPtr<ID3D12Resource> tempBufferObject;
|
||||
D3D12_HEAP_PROPERTIES d3dTempHeapProperties = {};
|
||||
@@ -146,26 +147,49 @@ bool D3D12Texture::InitializeFromData(ID3D12Device* device, ID3D12GraphicsComman
|
||||
return false;
|
||||
}
|
||||
|
||||
BYTE* pData;
|
||||
BYTE* pData = nullptr;
|
||||
tempBufferObject->Map(0, nullptr, reinterpret_cast<void**>(&pData));
|
||||
BYTE* pDstTempBuffer = reinterpret_cast<BYTE*>(pData + subresourceFootprint.Offset);
|
||||
const BYTE* pSrcData = reinterpret_cast<const BYTE*>(pixelData);
|
||||
const UINT sourceRowPitch = rowPitch > 0 ? rowPitch : static_cast<UINT>(rowSizeInBytes);
|
||||
for (UINT i = 0; i < rowUsed; i++) {
|
||||
memcpy(pDstTempBuffer + subresourceFootprint.Footprint.RowPitch * i, pSrcData + sourceRowPitch * i, rowSizeInBytes);
|
||||
const UINT sourceRowPitch = rowPitch > 0 ? rowPitch : static_cast<UINT>(subresourceRowSizes[0]);
|
||||
|
||||
size_t sourceOffset = 0;
|
||||
for (UINT subresourceIndex = 0; subresourceIndex < subresourceCount; ++subresourceIndex) {
|
||||
const D3D12_PLACED_SUBRESOURCE_FOOTPRINT& footprint = subresourceFootprints[subresourceIndex];
|
||||
const UINT rowCount = subresourceRowCounts[subresourceIndex];
|
||||
const UINT64 rowSizeInBytes = subresourceRowSizes[subresourceIndex];
|
||||
BYTE* pDstTempBuffer = pData + footprint.Offset;
|
||||
|
||||
for (UINT depthSlice = 0; depthSlice < footprint.Footprint.Depth; ++depthSlice) {
|
||||
for (UINT rowIndex = 0; rowIndex < rowCount; ++rowIndex) {
|
||||
const size_t srcRowOffset =
|
||||
sourceOffset +
|
||||
static_cast<size_t>(depthSlice) * static_cast<size_t>(sourceRowPitch) * rowCount +
|
||||
static_cast<size_t>(rowIndex) * sourceRowPitch;
|
||||
memcpy(
|
||||
pDstTempBuffer +
|
||||
static_cast<size_t>(depthSlice) * footprint.Footprint.RowPitch * rowCount +
|
||||
static_cast<size_t>(rowIndex) * footprint.Footprint.RowPitch,
|
||||
pSrcData + srcRowOffset,
|
||||
static_cast<size_t>(std::min<UINT64>(rowSizeInBytes, sourceRowPitch)));
|
||||
}
|
||||
}
|
||||
|
||||
sourceOffset += static_cast<size_t>(sourceRowPitch) * rowCount * footprint.Footprint.Depth;
|
||||
}
|
||||
tempBufferObject->Unmap(0, nullptr);
|
||||
|
||||
D3D12_TEXTURE_COPY_LOCATION dst = {};
|
||||
dst.pResource = m_resource.Get();
|
||||
dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||
dst.SubresourceIndex = 0;
|
||||
for (UINT subresourceIndex = 0; subresourceIndex < subresourceCount; ++subresourceIndex) {
|
||||
D3D12_TEXTURE_COPY_LOCATION dst = {};
|
||||
dst.pResource = m_resource.Get();
|
||||
dst.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||
dst.SubresourceIndex = subresourceIndex;
|
||||
|
||||
D3D12_TEXTURE_COPY_LOCATION src = {};
|
||||
src.pResource = tempBufferObject.Get();
|
||||
src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||
src.PlacedFootprint = subresourceFootprint;
|
||||
commandList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr);
|
||||
D3D12_TEXTURE_COPY_LOCATION src = {};
|
||||
src.pResource = tempBufferObject.Get();
|
||||
src.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT;
|
||||
src.PlacedFootprint = subresourceFootprints[subresourceIndex];
|
||||
commandList->CopyTextureRegion(&dst, 0, 0, 0, &src, nullptr);
|
||||
}
|
||||
|
||||
D3D12_RESOURCE_BARRIER barrier = {};
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
|
||||
@@ -2,6 +2,8 @@
|
||||
#include "XCEngine/RHI/OpenGL/OpenGLPipelineLayout.h"
|
||||
#include "XCEngine/RHI/OpenGL/OpenGLTextureUnitAllocator.h"
|
||||
#include "XCEngine/RHI/OpenGL/OpenGLResourceView.h"
|
||||
#include "XCEngine/RHI/OpenGL/OpenGLEnums.h"
|
||||
#include "XCEngine/RHI/OpenGL/OpenGLTexture.h"
|
||||
#include "XCEngine/RHI/OpenGL/OpenGLSampler.h"
|
||||
#include <glad/glad.h>
|
||||
|
||||
@@ -81,6 +83,7 @@ bool OpenGLDescriptorSet::Initialize(OpenGLTextureUnitAllocator* allocator, uint
|
||||
m_bindings[i].count = layout.bindings[i].count;
|
||||
m_bindings[i].textureUnits.resize(layout.bindings[i].count);
|
||||
m_bindings[i].textureIds.resize(layout.bindings[i].count, 0);
|
||||
m_bindings[i].textureTargets.resize(layout.bindings[i].count, GL_TEXTURE_2D);
|
||||
m_bindings[i].samplerIds.resize(layout.bindings[i].count, 0);
|
||||
|
||||
if (m_bindings[i].type != DescriptorType::SRV &&
|
||||
@@ -159,6 +162,9 @@ void OpenGLDescriptorSet::Update(uint32_t offset, RHIResourceView* view) {
|
||||
|
||||
OpenGLResourceView* glView = static_cast<OpenGLResourceView*>(view);
|
||||
binding->textureIds[0] = glView->GetTexture();
|
||||
if (const OpenGLTexture* texture = glView->GetTextureResource()) {
|
||||
binding->textureTargets[0] = static_cast<uint32_t>(ToOpenGL(texture->GetOpenGLType()));
|
||||
}
|
||||
}
|
||||
|
||||
void OpenGLDescriptorSet::UpdateSampler(uint32_t offset, RHISampler* sampler) {
|
||||
@@ -200,7 +206,9 @@ void OpenGLDescriptorSet::Bind() {
|
||||
|
||||
if (textureId != 0 && binding.type != DescriptorType::Sampler) {
|
||||
glActiveTexture(GL_TEXTURE0 + unit);
|
||||
glBindTexture(GL_TEXTURE_2D, textureId);
|
||||
glBindTexture(
|
||||
j < binding.textureTargets.size() ? binding.textureTargets[j] : GL_TEXTURE_2D,
|
||||
textureId);
|
||||
}
|
||||
|
||||
if (samplerId != 0) {
|
||||
@@ -246,7 +254,9 @@ void OpenGLDescriptorSet::BindWithPipelineLayout(const OpenGLPipelineLayout* pip
|
||||
glBindImageTexture(bindingPoint, textureId, 0, GL_FALSE, 0, GL_READ_WRITE, GL_RGBA8);
|
||||
} else if (binding.type != DescriptorType::Sampler) {
|
||||
glActiveTexture(GL_TEXTURE0 + bindingPoint);
|
||||
glBindTexture(GL_TEXTURE_2D, textureId);
|
||||
glBindTexture(
|
||||
i < binding.textureTargets.size() ? binding.textureTargets[i] : GL_TEXTURE_2D,
|
||||
textureId);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -276,7 +286,9 @@ void OpenGLDescriptorSet::Unbind() {
|
||||
uint32_t unit = binding.textureUnits[j];
|
||||
|
||||
glActiveTexture(GL_TEXTURE0 + unit);
|
||||
glBindTexture(GL_TEXTURE_2D, 0);
|
||||
glBindTexture(
|
||||
j < binding.textureTargets.size() ? binding.textureTargets[j] : GL_TEXTURE_2D,
|
||||
0);
|
||||
glBindSampler(unit, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,6 +7,34 @@
|
||||
namespace XCEngine {
|
||||
namespace RHI {
|
||||
|
||||
namespace {
|
||||
|
||||
size_t GetOpenGLFormatPixelSize(OpenGLFormat format) {
|
||||
switch (format) {
|
||||
case OpenGLFormat::R8:
|
||||
return 1;
|
||||
case OpenGLFormat::RG8:
|
||||
return 2;
|
||||
case OpenGLFormat::RGBA8:
|
||||
case OpenGLFormat::RGBA8_SRGB:
|
||||
return 4;
|
||||
case OpenGLFormat::RG32F:
|
||||
return sizeof(float) * 2;
|
||||
case OpenGLFormat::RGBA16F:
|
||||
return sizeof(uint16_t) * 4;
|
||||
case OpenGLFormat::RGBA32F:
|
||||
return sizeof(float) * 4;
|
||||
case OpenGLFormat::Depth24Stencil8:
|
||||
return 4;
|
||||
case OpenGLFormat::Depth32F:
|
||||
return sizeof(float);
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
OpenGLTexture::OpenGLTexture()
|
||||
: m_texture(0)
|
||||
, m_type(OpenGLTextureType::Texture2D)
|
||||
@@ -37,8 +65,22 @@ bool OpenGLTexture::Initialize(OpenGLTextureType type, int width, int height, in
|
||||
glBindTexture(target, m_texture);
|
||||
|
||||
if (type == OpenGLTextureType::TextureCube) {
|
||||
const size_t faceSize = static_cast<size_t>(width) * static_cast<size_t>(height) * GetOpenGLFormatPixelSize(format);
|
||||
const unsigned char* facePixels = reinterpret_cast<const unsigned char*>(data);
|
||||
for (int i = 0; i < 6; i++) {
|
||||
glTexImage2D(GL_TEXTURE_CUBE_MAP_POSITIVE_X + i, 0, internalFormat, width, height, 0, glFormat, glType, data);
|
||||
const void* faceData = (facePixels != nullptr && faceSize > 0)
|
||||
? facePixels + faceSize * static_cast<size_t>(i)
|
||||
: nullptr;
|
||||
glTexImage2D(
|
||||
GL_TEXTURE_CUBE_MAP_POSITIVE_X + i,
|
||||
0,
|
||||
internalFormat,
|
||||
width,
|
||||
height,
|
||||
0,
|
||||
glFormat,
|
||||
glType,
|
||||
faceData);
|
||||
}
|
||||
} else if (type == OpenGLTextureType::Texture1D) {
|
||||
glTexImage1D(GL_TEXTURE_1D, 0, internalFormat, width, 0, glFormat, glType, data);
|
||||
|
||||
@@ -396,7 +396,7 @@ bool BuiltinForwardPipeline::ExecuteForwardSkyboxPass(const RenderPassContext& p
|
||||
RHI::RHIResourceView* skyboxTextureView = ResolveTextureView(skyboxTexture);
|
||||
const bool useTexturedSkybox = sceneData.environment.HasMaterialSkybox() && skyboxTextureView != nullptr;
|
||||
if (skyboxTextureView == nullptr) {
|
||||
skyboxTextureView = m_fallbackTextureView;
|
||||
skyboxTextureView = m_fallbackTextureCubeView;
|
||||
}
|
||||
|
||||
const BuiltinSkyboxMaterialData skyboxMaterialData = BuildBuiltinSkyboxMaterialData(skyboxMaterial);
|
||||
@@ -553,29 +553,72 @@ bool BuiltinForwardPipeline::CreatePipelineResources(const RenderContext& contex
|
||||
return false;
|
||||
}
|
||||
|
||||
const unsigned char whitePixel[4] = { 255, 255, 255, 255 };
|
||||
RHI::TextureDesc textureDesc = {};
|
||||
textureDesc.width = 1;
|
||||
textureDesc.height = 1;
|
||||
textureDesc.depth = 1;
|
||||
textureDesc.mipLevels = 1;
|
||||
textureDesc.arraySize = 1;
|
||||
textureDesc.format = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
|
||||
textureDesc.textureType = static_cast<uint32_t>(RHI::TextureType::Texture2D);
|
||||
textureDesc.sampleCount = 1;
|
||||
textureDesc.sampleQuality = 0;
|
||||
textureDesc.flags = 0;
|
||||
m_fallbackTexture = context.device->CreateTexture(textureDesc, whitePixel, sizeof(whitePixel), 4);
|
||||
if (m_fallbackTexture == nullptr) {
|
||||
const unsigned char whitePixels2D[4] = {
|
||||
255, 255, 255, 255
|
||||
};
|
||||
RHI::TextureDesc fallback2DDesc = {};
|
||||
fallback2DDesc.width = 1;
|
||||
fallback2DDesc.height = 1;
|
||||
fallback2DDesc.depth = 1;
|
||||
fallback2DDesc.mipLevels = 1;
|
||||
fallback2DDesc.arraySize = 1;
|
||||
fallback2DDesc.format = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
|
||||
fallback2DDesc.textureType = static_cast<uint32_t>(RHI::TextureType::Texture2D);
|
||||
fallback2DDesc.sampleCount = 1;
|
||||
fallback2DDesc.sampleQuality = 0;
|
||||
fallback2DDesc.flags = 0;
|
||||
m_fallbackTexture2D = context.device->CreateTexture(
|
||||
fallback2DDesc,
|
||||
whitePixels2D,
|
||||
sizeof(whitePixels2D),
|
||||
sizeof(whitePixels2D));
|
||||
if (m_fallbackTexture2D == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RHI::ResourceViewDesc textureViewDesc = {};
|
||||
textureViewDesc.format = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
|
||||
textureViewDesc.dimension = RHI::ResourceViewDimension::Texture2D;
|
||||
textureViewDesc.mipLevel = 0;
|
||||
m_fallbackTextureView = context.device->CreateShaderResourceView(m_fallbackTexture, textureViewDesc);
|
||||
if (m_fallbackTextureView == nullptr) {
|
||||
RHI::ResourceViewDesc fallback2DViewDesc = {};
|
||||
fallback2DViewDesc.format = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
|
||||
fallback2DViewDesc.dimension = RHI::ResourceViewDimension::Texture2D;
|
||||
fallback2DViewDesc.mipLevel = 0;
|
||||
m_fallbackTexture2DView = context.device->CreateShaderResourceView(m_fallbackTexture2D, fallback2DViewDesc);
|
||||
if (m_fallbackTexture2DView == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const unsigned char whitePixelsCube[6 * 4] = {
|
||||
255, 255, 255, 255,
|
||||
255, 255, 255, 255,
|
||||
255, 255, 255, 255,
|
||||
255, 255, 255, 255,
|
||||
255, 255, 255, 255,
|
||||
255, 255, 255, 255
|
||||
};
|
||||
RHI::TextureDesc fallbackCubeDesc = {};
|
||||
fallbackCubeDesc.width = 1;
|
||||
fallbackCubeDesc.height = 1;
|
||||
fallbackCubeDesc.depth = 1;
|
||||
fallbackCubeDesc.mipLevels = 1;
|
||||
fallbackCubeDesc.arraySize = 6;
|
||||
fallbackCubeDesc.format = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
|
||||
fallbackCubeDesc.textureType = static_cast<uint32_t>(RHI::TextureType::TextureCube);
|
||||
fallbackCubeDesc.sampleCount = 1;
|
||||
fallbackCubeDesc.sampleQuality = 0;
|
||||
fallbackCubeDesc.flags = 0;
|
||||
m_fallbackTextureCube = context.device->CreateTexture(
|
||||
fallbackCubeDesc,
|
||||
whitePixelsCube,
|
||||
sizeof(whitePixelsCube),
|
||||
4);
|
||||
if (m_fallbackTextureCube == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RHI::ResourceViewDesc fallbackCubeViewDesc = {};
|
||||
fallbackCubeViewDesc.format = static_cast<uint32_t>(RHI::Format::R8G8B8A8_UNorm);
|
||||
fallbackCubeViewDesc.dimension = RHI::ResourceViewDimension::TextureCube;
|
||||
fallbackCubeViewDesc.mipLevel = 0;
|
||||
m_fallbackTextureCubeView = context.device->CreateShaderResourceView(m_fallbackTextureCube, fallbackCubeViewDesc);
|
||||
if (m_fallbackTextureCubeView == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -715,8 +758,8 @@ bool BuiltinForwardPipeline::CreateSkyboxResources(const RenderContext& context)
|
||||
}
|
||||
|
||||
m_skyboxSamplerSet.set->UpdateSampler(0, m_sampler);
|
||||
m_skyboxTextureSet.set->Update(0, m_fallbackTextureView);
|
||||
m_skyboxBoundTextureView = m_fallbackTextureView;
|
||||
m_skyboxTextureSet.set->Update(0, m_fallbackTextureCubeView);
|
||||
m_skyboxBoundTextureView = m_fallbackTextureCubeView;
|
||||
|
||||
m_skyboxPipelineState = m_device->CreatePipelineState(
|
||||
CreateSkyboxPipelineDesc(
|
||||
@@ -776,18 +819,30 @@ void BuiltinForwardPipeline::DestroyPipelineResources() {
|
||||
}
|
||||
m_passResourceLayouts.clear();
|
||||
|
||||
if (m_fallbackTextureView != nullptr) {
|
||||
m_fallbackTextureView->Shutdown();
|
||||
delete m_fallbackTextureView;
|
||||
m_fallbackTextureView = nullptr;
|
||||
}
|
||||
|
||||
DestroySkyboxResources();
|
||||
|
||||
if (m_fallbackTexture != nullptr) {
|
||||
m_fallbackTexture->Shutdown();
|
||||
delete m_fallbackTexture;
|
||||
m_fallbackTexture = nullptr;
|
||||
if (m_fallbackTextureCubeView != nullptr) {
|
||||
m_fallbackTextureCubeView->Shutdown();
|
||||
delete m_fallbackTextureCubeView;
|
||||
m_fallbackTextureCubeView = nullptr;
|
||||
}
|
||||
|
||||
if (m_fallbackTextureCube != nullptr) {
|
||||
m_fallbackTextureCube->Shutdown();
|
||||
delete m_fallbackTextureCube;
|
||||
m_fallbackTextureCube = nullptr;
|
||||
}
|
||||
|
||||
if (m_fallbackTexture2DView != nullptr) {
|
||||
m_fallbackTexture2DView->Shutdown();
|
||||
delete m_fallbackTexture2DView;
|
||||
m_fallbackTexture2DView = nullptr;
|
||||
}
|
||||
|
||||
if (m_fallbackTexture2D != nullptr) {
|
||||
m_fallbackTexture2D->Shutdown();
|
||||
delete m_fallbackTexture2D;
|
||||
m_fallbackTexture2D = nullptr;
|
||||
}
|
||||
|
||||
if (m_sampler != nullptr) {
|
||||
|
||||
@@ -516,7 +516,7 @@ RHI::RHIResourceView* BuiltinForwardPipeline::ResolveTextureView(
|
||||
const Resources::Material* material = ResolveMaterial(visibleItem);
|
||||
const Resources::Texture* texture = ResolveTexture(material);
|
||||
RHI::RHIResourceView* textureView = ResolveTextureView(texture);
|
||||
return textureView != nullptr ? textureView : m_fallbackTextureView;
|
||||
return textureView != nullptr ? textureView : m_fallbackTexture2DView;
|
||||
}
|
||||
|
||||
BuiltinForwardPipeline::LightingConstants BuiltinForwardPipeline::BuildLightingConstants(
|
||||
@@ -651,7 +651,7 @@ bool BuiltinForwardPipeline::DrawVisibleItem(
|
||||
|
||||
RHI::RHIResourceView* shadowMapTextureView = sceneData.lighting.HasMainDirectionalShadow()
|
||||
? sceneData.lighting.mainDirectionalShadow.shadowMap
|
||||
: m_fallbackTextureView;
|
||||
: m_fallbackTexture2DView;
|
||||
if (passLayout->shadowMapTexture.IsValid() && shadowMapTextureView == nullptr) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -14,14 +14,18 @@ void Texture::Release() {
|
||||
|
||||
bool Texture::Create(Core::uint32 width, Core::uint32 height, Core::uint32 depth,
|
||||
Core::uint32 mipLevels, TextureType type, TextureFormat format,
|
||||
const void* data, size_t dataSize) {
|
||||
const void* data, size_t dataSize, Core::uint32 arraySize) {
|
||||
m_width = width;
|
||||
m_height = height;
|
||||
m_depth = depth;
|
||||
m_mipLevels = mipLevels;
|
||||
m_textureType = type;
|
||||
m_format = format;
|
||||
m_arraySize = arraySize > 0
|
||||
? arraySize
|
||||
: (type == TextureType::TextureCube || type == TextureType::TextureCubeArray ? 6u : 1u);
|
||||
|
||||
m_pixelData.Clear();
|
||||
if (data && dataSize > 0) {
|
||||
m_pixelData.Resize(dataSize);
|
||||
std::memcpy(m_pixelData.Data(), data, dataSize);
|
||||
|
||||
@@ -22,9 +22,13 @@ Containers::String GetResourceNameFromPath(const Containers::String& path) {
|
||||
}
|
||||
|
||||
LoadResult CreateTextureResource(const Containers::String& path,
|
||||
TextureType textureType,
|
||||
TextureFormat format,
|
||||
Core::uint32 width,
|
||||
Core::uint32 height,
|
||||
Core::uint32 depth,
|
||||
Core::uint32 mipLevels,
|
||||
Core::uint32 arraySize,
|
||||
const void* pixelData,
|
||||
size_t pixelDataSize) {
|
||||
auto* texture = new Texture();
|
||||
@@ -38,12 +42,13 @@ LoadResult CreateTextureResource(const Containers::String& path,
|
||||
|
||||
if (!texture->Create(width,
|
||||
height,
|
||||
1,
|
||||
1,
|
||||
TextureType::Texture2D,
|
||||
depth,
|
||||
mipLevels,
|
||||
textureType,
|
||||
format,
|
||||
pixelData,
|
||||
pixelDataSize)) {
|
||||
pixelDataSize,
|
||||
arraySize)) {
|
||||
delete texture;
|
||||
return LoadResult(Containers::String("Failed to create texture resource: ") + path);
|
||||
}
|
||||
@@ -105,9 +110,18 @@ LoadResult LoadTextureArtifact(const Containers::String& path) {
|
||||
}
|
||||
|
||||
return CreateTextureResource(path,
|
||||
static_cast<TextureType>(header.textureType),
|
||||
static_cast<TextureFormat>(header.textureFormat),
|
||||
header.width,
|
||||
header.height,
|
||||
header.depth > 0 ? header.depth : 1u,
|
||||
header.mipLevels > 0 ? header.mipLevels : 1u,
|
||||
header.arraySize > 0
|
||||
? header.arraySize
|
||||
: (header.textureType == static_cast<Core::uint32>(TextureType::TextureCube) ||
|
||||
header.textureType == static_cast<Core::uint32>(TextureType::TextureCubeArray)
|
||||
? 6u
|
||||
: 1u),
|
||||
pixelData.Data(),
|
||||
pixelData.Size());
|
||||
}
|
||||
@@ -202,9 +216,13 @@ LoadResult TextureLoader::LoadFromMemory(
|
||||
4u *
|
||||
sizeof(float);
|
||||
LoadResult result = CreateTextureResource(path,
|
||||
TextureType::Texture2D,
|
||||
ResolveDecodedTextureFormat(settings, true),
|
||||
static_cast<Core::uint32>(width),
|
||||
static_cast<Core::uint32>(height),
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
pixels,
|
||||
pixelDataSize);
|
||||
stbi_image_free(pixels);
|
||||
@@ -226,9 +244,13 @@ LoadResult TextureLoader::LoadFromMemory(
|
||||
4u *
|
||||
sizeof(stbi_uc);
|
||||
LoadResult result = CreateTextureResource(path,
|
||||
TextureType::Texture2D,
|
||||
ResolveDecodedTextureFormat(settings, false),
|
||||
static_cast<Core::uint32>(width),
|
||||
static_cast<Core::uint32>(height),
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
pixels,
|
||||
pixelDataSize);
|
||||
stbi_image_free(pixels);
|
||||
|
||||
@@ -48,6 +48,9 @@ target_compile_definitions(rendering_integration_skybox_scene PRIVATE
|
||||
)
|
||||
|
||||
add_custom_command(TARGET rendering_integration_skybox_scene POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_directory
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/Res
|
||||
$<TARGET_FILE_DIR:rendering_integration_skybox_scene>/Res
|
||||
COMMAND ${CMAKE_COMMAND} -E copy_if_different
|
||||
${CMAKE_SOURCE_DIR}/tests/RHI/integration/compare_ppm.py
|
||||
$<TARGET_FILE_DIR:rendering_integration_skybox_scene>/
|
||||
|
||||
File diff suppressed because one or more lines are too long
Binary file not shown.
|
After Width: | Height: | Size: 50 KiB |
@@ -0,0 +1,11 @@
|
||||
Skybox source:
|
||||
Poly Haven - "farm_field_puresky"
|
||||
Page: https://polyhaven.com/a/farm_field_puresky
|
||||
Direct download used for preprocessing:
|
||||
https://dl.polyhaven.org/file/ph-assets/HDRIs/extra/Tonemapped%20JPG/farm_field_puresky.jpg
|
||||
|
||||
License:
|
||||
CC0 1.0 Universal (public domain), as provided by Poly Haven.
|
||||
|
||||
Repository note:
|
||||
The file committed here is a resized derivative used only for automated rendering tests.
|
||||
@@ -1,3 +1,6 @@
|
||||
#define NOMINMAX
|
||||
#include <windows.h>
|
||||
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "../RenderingIntegrationMain.h"
|
||||
@@ -19,12 +22,16 @@
|
||||
#include <XCEngine/Resources/Mesh/Mesh.h>
|
||||
#include <XCEngine/Resources/Shader/Shader.h>
|
||||
#include <XCEngine/Resources/Texture/Texture.h>
|
||||
#include <XCEngine/Resources/Texture/TextureImportSettings.h>
|
||||
#include <XCEngine/Resources/Texture/TextureLoader.h>
|
||||
#include <XCEngine/RHI/RHITexture.h>
|
||||
#include <XCEngine/Scene/Scene.h>
|
||||
|
||||
#include "../../../RHI/integration/fixtures/RHIIntegrationFixture.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cmath>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
@@ -42,78 +49,134 @@ constexpr const char* kOpenGLScreenshot = "skybox_scene_opengl.ppm";
|
||||
constexpr const char* kVulkanScreenshot = "skybox_scene_vulkan.ppm";
|
||||
constexpr uint32_t kFrameWidth = 1280;
|
||||
constexpr uint32_t kFrameHeight = 720;
|
||||
constexpr uint32_t kSkyboxFaceSize = 256;
|
||||
constexpr float kPi = 3.14159265358979323846f;
|
||||
|
||||
Texture* CreatePanoramicSkyboxTexture() {
|
||||
constexpr uint32_t kTextureWidth = 256;
|
||||
constexpr uint32_t kTextureHeight = 128;
|
||||
std::filesystem::path GetExecutableDirectory() {
|
||||
char exePath[MAX_PATH] = {};
|
||||
const DWORD length = GetModuleFileNameA(nullptr, exePath, MAX_PATH);
|
||||
if (length == 0 || length >= MAX_PATH) {
|
||||
return std::filesystem::current_path();
|
||||
}
|
||||
|
||||
return std::filesystem::path(exePath).parent_path();
|
||||
}
|
||||
|
||||
std::filesystem::path ResolveRuntimePath(const char* relativePath) {
|
||||
return GetExecutableDirectory() / relativePath;
|
||||
}
|
||||
|
||||
Vector3 ComputeCubemapDirection(uint32_t faceIndex, float s, float t) {
|
||||
switch (faceIndex) {
|
||||
case 0:
|
||||
return Vector3(1.0f, -t, -s).Normalized();
|
||||
case 1:
|
||||
return Vector3(-1.0f, -t, s).Normalized();
|
||||
case 2:
|
||||
return Vector3(s, 1.0f, t).Normalized();
|
||||
case 3:
|
||||
return Vector3(s, -1.0f, -t).Normalized();
|
||||
case 4:
|
||||
return Vector3(s, -t, 1.0f).Normalized();
|
||||
case 5:
|
||||
default:
|
||||
return Vector3(-s, -t, -1.0f).Normalized();
|
||||
}
|
||||
}
|
||||
|
||||
void SamplePanoramaNearest(
|
||||
const Texture& panorama,
|
||||
float u,
|
||||
float v,
|
||||
unsigned char outRgba[4]) {
|
||||
const uint32_t width = panorama.GetWidth();
|
||||
const uint32_t height = panorama.GetHeight();
|
||||
const auto* pixels = static_cast<const unsigned char*>(panorama.GetPixelData());
|
||||
if (pixels == nullptr || width == 0 || height == 0) {
|
||||
outRgba[0] = 255;
|
||||
outRgba[1] = 255;
|
||||
outRgba[2] = 255;
|
||||
outRgba[3] = 255;
|
||||
return;
|
||||
}
|
||||
|
||||
u = u - std::floor(u);
|
||||
v = std::clamp(v, 0.0f, 1.0f);
|
||||
|
||||
const uint32_t x = std::min<uint32_t>(
|
||||
static_cast<uint32_t>(u * static_cast<float>(width - 1) + 0.5f),
|
||||
width - 1);
|
||||
const uint32_t y = std::min<uint32_t>(
|
||||
static_cast<uint32_t>(v * static_cast<float>(height - 1) + 0.5f),
|
||||
height - 1);
|
||||
const size_t pixelIndex = (static_cast<size_t>(y) * static_cast<size_t>(width) + x) * 4u;
|
||||
outRgba[0] = pixels[pixelIndex + 0];
|
||||
outRgba[1] = pixels[pixelIndex + 1];
|
||||
outRgba[2] = pixels[pixelIndex + 2];
|
||||
outRgba[3] = pixels[pixelIndex + 3];
|
||||
}
|
||||
|
||||
Texture* CreateCubemapSkyboxTexture(const std::filesystem::path& panoramaPath) {
|
||||
TextureLoader loader;
|
||||
TextureImportSettings settings;
|
||||
settings.SetSRGB(true);
|
||||
|
||||
LoadResult loadResult = loader.Load(panoramaPath.string().c_str(), &settings);
|
||||
if (!loadResult || loadResult.resource == nullptr) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::unique_ptr<Texture> panorama(static_cast<Texture*>(loadResult.resource));
|
||||
if (!panorama || panorama->GetPixelData() == nullptr || panorama->GetWidth() == 0 || panorama->GetHeight() == 0) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
auto* texture = new Texture();
|
||||
IResource::ConstructParams params = {};
|
||||
params.name = "SkyboxPanoramicTexture";
|
||||
params.path = "Tests/Rendering/SkyboxScene/skybox_panorama.texture";
|
||||
params.name = "SkyboxCubemapTexture";
|
||||
params.path = "Tests/Rendering/SkyboxScene/skybox_cubemap.texture";
|
||||
params.guid = ResourceGUID::Generate(params.path);
|
||||
texture->Initialize(params);
|
||||
|
||||
std::vector<float> pixels(static_cast<size_t>(kTextureWidth) * static_cast<size_t>(kTextureHeight) * 4u, 1.0f);
|
||||
for (uint32_t y = 0; y < kTextureHeight; ++y) {
|
||||
for (uint32_t x = 0; x < kTextureWidth; ++x) {
|
||||
const float u = static_cast<float>(x) / static_cast<float>(kTextureWidth - 1u);
|
||||
const float v = static_cast<float>(y) / static_cast<float>(kTextureHeight - 1u);
|
||||
std::vector<unsigned char> pixels(
|
||||
static_cast<size_t>(kSkyboxFaceSize) * static_cast<size_t>(kSkyboxFaceSize) * 4u * 6u,
|
||||
255u);
|
||||
for (uint32_t faceIndex = 0; faceIndex < 6u; ++faceIndex) {
|
||||
for (uint32_t y = 0; y < kSkyboxFaceSize; ++y) {
|
||||
for (uint32_t x = 0; x < kSkyboxFaceSize; ++x) {
|
||||
const float s = (2.0f * (static_cast<float>(x) + 0.5f) / static_cast<float>(kSkyboxFaceSize)) - 1.0f;
|
||||
const float t = (2.0f * (static_cast<float>(y) + 0.5f) / static_cast<float>(kSkyboxFaceSize)) - 1.0f;
|
||||
const Vector3 direction = ComputeCubemapDirection(faceIndex, s, t);
|
||||
|
||||
Vector3 color = Vector3(0.05f, 0.08f, 0.14f);
|
||||
if (v < 0.42f) {
|
||||
const float t = v / 0.42f;
|
||||
color = Vector3(
|
||||
0.08f + 0.42f * (1.0f - t),
|
||||
0.16f + 0.50f * (1.0f - t),
|
||||
0.34f + 0.58f * (1.0f - t));
|
||||
} else if (v < 0.58f) {
|
||||
const float t = (v - 0.42f) / 0.16f;
|
||||
color = Vector3(
|
||||
0.92f - 0.24f * t,
|
||||
0.74f - 0.18f * t,
|
||||
0.36f - 0.12f * t);
|
||||
} else {
|
||||
const float t = (v - 0.58f) / 0.42f;
|
||||
color = Vector3(
|
||||
0.12f - 0.06f * t,
|
||||
0.10f - 0.05f * t,
|
||||
0.09f - 0.04f * t);
|
||||
}
|
||||
const float u = std::atan2(direction.z, direction.x) / (2.0f * kPi) + 0.5f;
|
||||
const float v = std::acos(std::clamp(direction.y, -1.0f, 1.0f)) / kPi;
|
||||
|
||||
const float sunDx = u - 0.22f;
|
||||
const float sunDy = v - 0.34f;
|
||||
const float sun = std::exp(-(sunDx * sunDx + sunDy * sunDy) * 1450.0f) * 4.0f;
|
||||
color += Vector3(1.0f, 0.82f, 0.58f) * sun;
|
||||
unsigned char rgba[4] = {};
|
||||
SamplePanoramaNearest(*panorama, u, v, rgba);
|
||||
|
||||
if (u > 0.62f && u < 0.68f && v > 0.46f && v < 0.82f) {
|
||||
color = Vector3(0.10f, 0.52f, 0.96f);
|
||||
const size_t pixelIndex =
|
||||
((static_cast<size_t>(faceIndex) * static_cast<size_t>(kSkyboxFaceSize) *
|
||||
static_cast<size_t>(kSkyboxFaceSize)) +
|
||||
(static_cast<size_t>(y) * static_cast<size_t>(kSkyboxFaceSize) + x)) * 4u;
|
||||
pixels[pixelIndex + 0] = rgba[0];
|
||||
pixels[pixelIndex + 1] = rgba[1];
|
||||
pixels[pixelIndex + 2] = rgba[2];
|
||||
pixels[pixelIndex + 3] = 255u;
|
||||
}
|
||||
if (u > 0.34f && u < 0.39f && v > 0.50f && v < 0.86f) {
|
||||
color = Vector3(0.88f, 0.24f, 0.19f);
|
||||
}
|
||||
if (u > 0.78f && u < 0.92f && v > 0.60f && v < 0.78f) {
|
||||
color = Vector3(0.22f, 0.70f, 0.28f);
|
||||
}
|
||||
|
||||
const size_t pixelIndex = (static_cast<size_t>(y) * static_cast<size_t>(kTextureWidth) + x) * 4u;
|
||||
pixels[pixelIndex + 0] = color.x;
|
||||
pixels[pixelIndex + 1] = color.y;
|
||||
pixels[pixelIndex + 2] = color.z;
|
||||
pixels[pixelIndex + 3] = 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
const size_t pixelDataSize = pixels.size() * sizeof(float);
|
||||
const size_t pixelDataSize = pixels.size();
|
||||
if (!texture->Create(
|
||||
kTextureWidth,
|
||||
kTextureHeight,
|
||||
kSkyboxFaceSize,
|
||||
kSkyboxFaceSize,
|
||||
1,
|
||||
1,
|
||||
XCEngine::Resources::TextureType::Texture2D,
|
||||
XCEngine::Resources::TextureFormat::RGBA32_FLOAT,
|
||||
XCEngine::Resources::TextureType::TextureCube,
|
||||
panorama->GetFormat(),
|
||||
pixels.data(),
|
||||
pixelDataSize)) {
|
||||
pixelDataSize,
|
||||
6)) {
|
||||
delete texture;
|
||||
return nullptr;
|
||||
}
|
||||
@@ -184,14 +247,14 @@ Material* CreateMaterial(
|
||||
Material* CreateSkyboxMaterial(Texture* texture) {
|
||||
auto* material = new Material();
|
||||
IResource::ConstructParams params = {};
|
||||
params.name = "SkyboxPanoramicMaterial";
|
||||
params.path = "Tests/Rendering/SkyboxScene/skybox_panorama.mat";
|
||||
params.name = "SkyboxCubemapMaterial";
|
||||
params.path = "Tests/Rendering/SkyboxScene/skybox_cubemap.mat";
|
||||
params.guid = ResourceGUID::Generate(params.path);
|
||||
material->Initialize(params);
|
||||
material->SetShader(ResourceManager::Get().Load<Shader>(GetBuiltinSkyboxShaderPath()));
|
||||
material->SetTexture("_MainTex", ResourceHandle<Texture>(texture));
|
||||
material->SetTexture("_Tex", ResourceHandle<Texture>(texture));
|
||||
material->SetFloat4("_Tint", Vector4(1.0f, 1.0f, 1.0f, 1.0f));
|
||||
material->SetFloat("_Exposure", 1.05f);
|
||||
material->SetFloat("_Exposure", 1.0f);
|
||||
material->SetFloat("_Rotation", 0.0f);
|
||||
return material;
|
||||
}
|
||||
@@ -341,7 +404,7 @@ void SkyboxSceneTest::BuildScene() {
|
||||
camera->SetFarClipPlane(20.0f);
|
||||
camera->SetClearColor(XCEngine::Math::Color(0.01f, 0.01f, 0.01f, 1.0f));
|
||||
camera->SetSkyboxEnabled(true);
|
||||
Texture* skyboxTexture = CreatePanoramicSkyboxTexture();
|
||||
Texture* skyboxTexture = CreateCubemapSkyboxTexture(ResolveRuntimePath("Res/skyboxes/farm_field_puresky_1024.jpg"));
|
||||
ASSERT_NE(skyboxTexture, nullptr);
|
||||
Material* skyboxMaterial = CreateSkyboxMaterial(skyboxTexture);
|
||||
ASSERT_NE(skyboxMaterial, nullptr);
|
||||
|
||||
@@ -173,10 +173,10 @@ TEST(BuiltinForwardPipeline_Test, BuiltinSkyboxShaderDeclaresExplicitEnvironment
|
||||
EXPECT_EQ(rotation->type, ShaderPropertyType::Float);
|
||||
EXPECT_EQ(rotation->semantic, "Rotation");
|
||||
|
||||
const ShaderPropertyDesc* panoramic = shader->FindProperty("_MainTex");
|
||||
ASSERT_NE(panoramic, nullptr);
|
||||
EXPECT_EQ(panoramic->type, ShaderPropertyType::Texture2D);
|
||||
EXPECT_EQ(panoramic->semantic, "SkyboxTexture");
|
||||
const ShaderPropertyDesc* cubemap = shader->FindProperty("_Tex");
|
||||
ASSERT_NE(cubemap, nullptr);
|
||||
EXPECT_EQ(cubemap->type, ShaderPropertyType::TextureCube);
|
||||
EXPECT_EQ(cubemap->semantic, "SkyboxTexture");
|
||||
|
||||
EXPECT_EQ(pass->resources[0].semantic, "Environment");
|
||||
EXPECT_EQ(pass->resources[0].type, ShaderResourceType::ConstantBuffer);
|
||||
@@ -189,7 +189,7 @@ TEST(BuiltinForwardPipeline_Test, BuiltinSkyboxShaderDeclaresExplicitEnvironment
|
||||
EXPECT_EQ(pass->resources[1].binding, 0u);
|
||||
|
||||
EXPECT_EQ(pass->resources[2].semantic, "SkyboxTexture");
|
||||
EXPECT_EQ(pass->resources[2].type, ShaderResourceType::Texture2D);
|
||||
EXPECT_EQ(pass->resources[2].type, ShaderResourceType::TextureCube);
|
||||
EXPECT_EQ(pass->resources[2].set, 2u);
|
||||
EXPECT_EQ(pass->resources[2].binding, 0u);
|
||||
|
||||
|
||||
@@ -34,4 +34,30 @@ TEST(Texture, IsValid) {
|
||||
EXPECT_FALSE(texture.IsValid());
|
||||
}
|
||||
|
||||
TEST(Texture, CreatePreservesExplicitTextureCubeArraySize) {
|
||||
Texture texture;
|
||||
const unsigned char pixels[6 * 4] = {
|
||||
255, 255, 255, 255,
|
||||
255, 255, 255, 255,
|
||||
255, 255, 255, 255,
|
||||
255, 255, 255, 255,
|
||||
255, 255, 255, 255,
|
||||
255, 255, 255, 255
|
||||
};
|
||||
|
||||
ASSERT_TRUE(texture.Create(
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
1,
|
||||
TextureType::TextureCube,
|
||||
TextureFormat::RGBA8_UNORM,
|
||||
pixels,
|
||||
sizeof(pixels),
|
||||
6));
|
||||
EXPECT_EQ(texture.GetTextureType(), TextureType::TextureCube);
|
||||
EXPECT_EQ(texture.GetArraySize(), 6u);
|
||||
EXPECT_EQ(texture.GetPixelDataSize(), sizeof(pixels));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Reference in New Issue
Block a user