diff --git a/README.md b/README.md index 41a8fc8..33695f9 100644 --- a/README.md +++ b/README.md @@ -275,6 +275,16 @@ ID3D12CommandList* pCommandLists[] = { commandLists.m_beforeDrawCommands, m_comm m_commandQueue->ExecuteCommandLists(_countof(pCommandLists), pCommandLists); ``` +## Log + +- 2021-06-21: initial commit +- 2021-07-23: use windows events to reduce cpu overhead +- 2021-08-10: use WaitOnAddress to further reduce cpu overhead. some 16k x 16k textures (BC7 format) posted as "release 1". +- 2021-08-28: proof-of-concept culling: textures for objects behind view are evicted +- 2021-09-20: fixed race condition that could result in hang on exit +- 2021-10-21: code refactor to improve sampler feedback streaming library API +- 2021-12-03: added BC1 asset collection as "release 2." All texture assets (.xet files) can reside in the same directory despite format differences, and can co-exist in the same GPU heap. Also minor source tweaks, including fix to not cull base "terrain" object. + ## License Sample and its code provided under MIT license, please see [LICENSE](/LICENSE). All third-party source code provided under their own respective and MIT-compatible Open Source licenses. diff --git a/TileUpdateManager/DataUploader.cpp b/TileUpdateManager/DataUploader.cpp index b22be83..0c8b976 100644 --- a/TileUpdateManager/DataUploader.cpp +++ b/TileUpdateManager/DataUploader.cpp @@ -190,7 +190,7 @@ void Streaming::DataUploader::StopThreads() //----------------------------------------------------------------------------- void Streaming::DataUploader::FlushCommands() { - DebugPrint(m_updateListFreeCount.load(), " DU flush\n"); + DebugPrint("DataUploader Flush ", m_updateListFreeCount.load(), "/", m_updateLists.size(), " batches freed\n"); while (m_updateListFreeCount.load() < m_updateLists.size()) { _mm_pause(); diff --git a/src/Gui.cpp b/src/Gui.cpp index b5a33fa..9ebffbc 100644 --- a/src/Gui.cpp +++ b/src/Gui.cpp @@ -39,7 +39,7 @@ // NOTE: this doesn't allocate any resources. it relies on calling function to set any heaps //----------------------------------------------------------------------------- Gui::Gui(HWND in_hWnd, ID3D12Device* in_pDevice, - ID3D12DescriptorHeap* in_pSrvHeap, const UINT in_rootSigSlot, + ID3D12DescriptorHeap* in_pSrvHeap, const UINT in_descriptorHeapOffset, const UINT in_swapChainBufferCount, const DXGI_FORMAT in_swapChainFormat, const std::wstring& in_adapterDescription, CommandLineArgs& in_args) : m_initialArgs(in_args) @@ -50,16 +50,16 @@ Gui::Gui(HWND in_hWnd, ID3D12Device* in_pDevice, { IMGUI_CHECKVERSION(); ImGui::CreateContext(); - ImGuiIO& io = ImGui::GetIO(); (void)io; + ImGuiIO& io = ImGui::GetIO(); io.ConfigFlags |= ImGuiConfigFlags_NavEnableSetMousePos + ImGuiBackendFlags_HasSetMousePos; // Enable Keyboard Controls ImGui::StyleColorsDark(); ImGui_ImplWin32_Init(in_hWnd); CD3DX12_CPU_DESCRIPTOR_HANDLE cpu(in_pSrvHeap->GetCPUDescriptorHandleForHeapStart(), - in_rootSigSlot, in_pDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)); + in_descriptorHeapOffset, in_pDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)); CD3DX12_GPU_DESCRIPTOR_HANDLE gpu(in_pSrvHeap->GetGPUDescriptorHandleForHeapStart(), - in_rootSigSlot, in_pDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)); + in_descriptorHeapOffset, in_pDevice->GetDescriptorHandleIncrementSize(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV)); ImGui_ImplDX12_Init(in_pDevice, in_swapChainBufferCount, in_swapChainFormat, in_pSrvHeap, cpu, gpu); ImGui_ImplDX12_CreateDeviceObjects(); diff --git a/src/Gui.h b/src/Gui.h index 902800f..b498521 100644 --- a/src/Gui.h +++ b/src/Gui.h @@ -34,7 +34,7 @@ class Gui { public: Gui(HWND in_hWnd, ID3D12Device* in_pDevice, - ID3D12DescriptorHeap* in_pSrvHeap, const UINT in_rootSigSlot, + ID3D12DescriptorHeap* in_pSrvHeap, const UINT in_descriptorHeapOffset, const UINT in_swapChainBufferCount, const DXGI_FORMAT in_swapChainFormat, const std::wstring& in_adapterDescription, CommandLineArgs& in_args); ~Gui(); diff --git a/src/Scene.cpp b/src/Scene.cpp index 24164f7..8f6209d 100644 --- a/src/Scene.cpp +++ b/src/Scene.cpp @@ -275,30 +275,26 @@ void Scene::MoveView(int in_x, int in_y, int in_z) //----------------------------------------------------------------------------- void Scene::RotateView(float in_x, float in_y, float in_z) { - XMMATRIX rotation = XMMatrixRotationRollPitchYaw(in_x, 0, in_z); + XMMATRIX rotation; - if (in_y) + // NOTE: locking the "up" axis feels great when navigating the terrain + // however, it breaks the controls when flying to other planets + if (m_args.m_cameraUpLock) { - // NOTE: locking the "up" axis feels great when navigating the terrain - // however, it breaks the controls when flying to other planets - XMMATRIX rotY = XMMatrixIdentity(); - if (m_args.m_cameraUpLock) - { - // this prevents spin while panning the terrain, but breaks if the user intentionally rotates in Z - rotY = XMMatrixRotationAxis(XMVectorSet(0, 1, 0, 1), in_y); - } - else - { - // this rotates correctly with any z axis rotation, but "up" can drift: - XMVECTOR yAxis = m_viewMatrixInverse.r[1]; - rotY = XMMatrixRotationNormal(yAxis, in_y); - } + XMVECTOR yAxis = XMVectorSet(0, 1, 0, 1); + XMMATRIX rotY = XMMatrixRotationAxis(yAxis, in_y); XMVECTOR xLate = XMVectorSetW(m_viewMatrixInverse.r[3], 0); rotY = XMMatrixMultiply(XMMatrixTranslationFromVector(-xLate), rotY); rotY = XMMatrixMultiply(rotY, XMMatrixTranslationFromVector(xLate)); m_viewMatrix = XMMatrixMultiply(rotY, m_viewMatrix); + + rotation = XMMatrixRotationRollPitchYaw(in_x, 0, in_z); + } + else + { + rotation = XMMatrixRotationRollPitchYaw(in_x, in_y, in_z); } m_viewMatrix = XMMatrixMultiply(m_viewMatrix, rotation); @@ -989,7 +985,8 @@ void Scene::CreateConstantBuffers() CD3DX12_RANGE readRange(0, bufferSize); ThrowIfFailed(m_frameConstantBuffer->Map(0, &readRange, reinterpret_cast(&m_pFrameConstantData))); - m_pFrameConstantData->g_lightDir = XMFLOAT4(-0.449135751f, 0.656364977f, 0.25f, 0); + m_pFrameConstantData->g_lightDir = XMFLOAT4(-0.538732767f, 0.787301660f, 0.299871892f, 0); + XMStoreFloat4(&m_pFrameConstantData->g_lightDir, XMVector4Normalize(XMLoadFloat4(&m_pFrameConstantData->g_lightDir))); m_pFrameConstantData->g_lightColor = XMFLOAT4(1, 1, 1, 40.0f); m_pFrameConstantData->g_specColor = XMFLOAT4(1, 1, 1, 1); @@ -1153,7 +1150,9 @@ void Scene::DrawObjects() // FIXME: want proper frustum culling here float w = XMVectorGetW(o->GetCombinedMatrix().r[3]); - bool visible = (w > 0) || (o == m_pSky); + // never cull the sky + // also never cull the terrain object, or will see incorrect behavior when inspecting closely + bool visible = (w > 0) || (o == m_pSky) || (o == m_pTerrainSceneObject); // get sampler feedback for this object? bool queueFeedback = false; @@ -1507,6 +1506,7 @@ void Scene::StartScene() else { m_pFrameConstantData->g_lightDir = XMFLOAT4(-0.449135751f, 0.656364977f, 0.25f, 0); + XMStoreFloat4(&m_pFrameConstantData->g_lightDir, XMVector4Normalize(XMLoadFloat4(&m_pFrameConstantData->g_lightDir))); } } @@ -1531,7 +1531,7 @@ void Scene::DrawUI(float in_cpuProcessFeedbackTime) UINT numMips = areaHeight / (UINT)minDim; if (numMips > 1) { - DirectX::XMFLOAT2 windowPos = DirectX::XMFLOAT2(m_viewport.Width - minDim, minDim); + DirectX::XMFLOAT2 windowPos = DirectX::XMFLOAT2(m_viewport.Width - minDim, 0); m_pTextureViewer->Draw(m_commandList.Get(), windowPos, windowSize, m_viewport, m_args.m_visualizationBaseMip, numMips - 1, @@ -1541,7 +1541,7 @@ void Scene::DrawUI(float in_cpuProcessFeedbackTime) else { UINT numMips = UINT(m_viewport.Width) / (UINT)minDim; - DirectX::XMFLOAT2 windowPos = DirectX::XMFLOAT2(0, minDim); + DirectX::XMFLOAT2 windowPos = DirectX::XMFLOAT2(0, 0); m_pTextureViewer->Draw(m_commandList.Get(), windowPos, windowSize, m_viewport, m_args.m_visualizationBaseMip, numMips, diff --git a/src/SceneObject.cpp b/src/SceneObject.cpp index 10e31bc..976fa7b 100644 --- a/src/SceneObject.cpp +++ b/src/SceneObject.cpp @@ -248,11 +248,9 @@ void SceneObjects::BaseObject::SetModelConstants(ModelConstantData& out_modelCon { out_modelConstantData.g_combinedTransform = m_combinedMatrix; - DirectX::XMVECTOR pDet; - DirectX::XMMATRIX worldInverse = XMMatrixInverse(&pDet, m_matrix); - out_modelConstantData.g_worldTransform = worldInverse; + out_modelConstantData.g_worldTransform = m_matrix; - DirectX::XMVECTOR vEyePt = XMVector4Transform(in_viewInverse.r[3], worldInverse); + DirectX::XMVECTOR vEyePt = in_viewInverse.r[3]; DirectX::XMStoreFloat4(&(out_modelConstantData.g_eyePos), vEyePt); @@ -689,8 +687,6 @@ void SceneObjects::Sky::SetModelConstants(ModelConstantData& out_modelConstantDa out_modelConstantData.g_combinedTransform = m_matrix * view * in_projection; - DirectX::XMVECTOR pDet; - DirectX::XMMATRIX worldInverse = XMMatrixInverse(&pDet, m_matrix); out_modelConstantData.g_worldTransform = DirectX::XMMatrixIdentity(); out_modelConstantData.g_minmipmapWidth = m_pStreamingResource->GetMinMipMapWidth(); diff --git a/src/TextureViewer.cpp b/src/TextureViewer.cpp index f0951a3..66ed520 100644 --- a/src/TextureViewer.cpp +++ b/src/TextureViewer.cpp @@ -265,7 +265,7 @@ void TextureViewer::DrawWindows(ID3D12GraphicsCommandList* in_pCL, D3D12_VIEWPOR } //----------------------------------------------------------------------------- -// note: screen space is -1,-1 to 1,1 +// draw the rectangle //----------------------------------------------------------------------------- void TextureViewer::Draw(ID3D12GraphicsCommandList* in_pCL, DirectX::XMFLOAT2 in_position, DirectX::XMFLOAT2 in_windowDim, @@ -284,8 +284,8 @@ void TextureViewer::Draw(ID3D12GraphicsCommandList* in_pCL, } ConstantBuffer* pConstants = (ConstantBuffer*)m_constants.data(); - pConstants->x = 2 * float(in_position.x) / in_viewPort.Width; - pConstants->y = 2 * float(in_position.y) / in_viewPort.Height; + pConstants->x = float(in_position.x) / in_viewPort.Width; + pConstants->y = float(in_position.y) / in_viewPort.Height; pConstants->width = in_windowDim.x / in_viewPort.Width; pConstants->height = in_windowDim.y / in_viewPort.Height; diff --git a/src/TextureViewer.h b/src/TextureViewer.h index 8f2c82c..4e241c0 100644 --- a/src/TextureViewer.h +++ b/src/TextureViewer.h @@ -26,7 +26,9 @@ #pragma once -// creates a number of windows showing mips of a provided texture +// creates a windows per mip of a texture +// screen coordinate system: (0, 0) is bottom-left. like normalized device space, (1, 1) is top-right +// u,v coordinates: (0, 0) is top-left. like images, byte 0 is top left class TextureViewer { diff --git a/src/TextureViewer.hlsl b/src/TextureViewer.hlsl index 20000d5..0e26db9 100644 --- a/src/TextureViewer.hlsl +++ b/src/TextureViewer.hlsl @@ -28,7 +28,7 @@ struct VS_OUT { float4 pos : SV_POSITION; float2 uv : TEXCOORD0; - nointerpolation float mipLevel: OUTPUT; + nointerpolation float mipLevel : OUTPUT; }; cbuffer cb0 @@ -41,39 +41,46 @@ cbuffer cb0 VS_OUT vs(uint vertexID : SV_VertexID) { - VS_OUT output; + // remember: normalized screen space is [-1,-1] [1,1], so everything is doubled float level = vertexID >> 2; vertexID &= 3; - // normalized screen space is [-1,-1] [1,1], so everything is doubled - // this forms a rect 0,0 2,0 0,2 2,2 - float2 grid = float2((vertexID & 1) << 1, vertexID & 2); + + // this forms a rect 0,0 2,0 0,2 2,2 : tri-strip starting clockwise + //float2 grid = float2((vertexID & 1) << 1, vertexID & 2); + + // this forms a rect 0,0 0,2 2,0 2,2 : tri-strip starting counter-clockwise + float2 grid = float2(vertexID & 2, (vertexID & 1) << 1); float width = g_viewPosition.z; - float height = -g_viewPosition.w; + float height = g_viewPosition.w; - // scale and shift window to bottom left of screen - output.pos = float4(grid * float2(width,height) + float2(-1.0f, -1.0), 0.0f, 1.0f); + VS_OUT output; + + // scale window and translate to bottom left of screen + output.pos = float4((grid * float2(width, height)) + float2(-1.0f, -1.0), 0.0f, 1.0f); // horizontal or vertical arrangement if (g_vertical) { - height -= g_gap; + height += g_gap; - output.pos.x += g_viewPosition.x; - output.pos.y += g_viewPosition.y - (height * level * 2); + output.pos.x += 2 * g_viewPosition.x; + output.pos.y += 2 * (g_viewPosition.y + (height * level)); } else { width += g_gap; - output.pos.x += g_viewPosition.x + (width * level * 2); - output.pos.y += g_viewPosition.y; + output.pos.x += 2 * (g_viewPosition.x + (width * level)); + output.pos.y += 2 * g_viewPosition.y; } // uv from 0,0 to 1,1 output.uv.xy = 0.5f * grid; + output.uv.y = 1 - output.uv.y; // the window has 0,0 as bottom-left. the image u,v should have v = 0 in the top-left. output.mipLevel = g_visBaseMip + level; + return output; } @@ -84,5 +91,5 @@ float4 ps(VS_OUT input) : SV_Target { float4 diffuse = g_texture2D.SampleLevel(g_sampler, input.uv.xy, input.mipLevel); - return float4(diffuse.xyz, 1.0f); + return float4(diffuse.xyz, 1.0f); } diff --git a/src/shaders/terrainPS.hlsl b/src/shaders/terrainPS.hlsl index 0e0a32b..d61d4be 100644 --- a/src/shaders/terrainPS.hlsl +++ b/src/shaders/terrainPS.hlsl @@ -38,9 +38,9 @@ SamplerState g_sampler : register(s0); float3 evaluateLight(in float3 normal, in float3 reflected) { // directional light - float3 pointToLight = normalize(g_lightDir.xyz); + float3 pointToLight = g_lightDir.xyz; - float diffuse = saturate(dot(g_lightDir.xyz, normal)); + float diffuse = saturate(dot(pointToLight, normal)); float specDot = saturate(dot(reflected, pointToLight)); float specular = pow(specDot, 2 * g_lightColor.a); diff --git a/src/shaders/terrainVS.hlsl b/src/shaders/terrainVS.hlsl index 4a891ec..e0c1bc1 100644 --- a/src/shaders/terrainVS.hlsl +++ b/src/shaders/terrainVS.hlsl @@ -70,13 +70,14 @@ VS_OUT vs(VS_IN input) VS_OUT result; result.pos = mul(g_combinedTransform, float4(input.pos, 1.0f)); - // rotate normal into light coordinate frame - result.normal = normalize(mul(float4(input.normal, 0.0f), g_worldTransform).xyz); + // rotate normal into light coordinate frame (world) + result.normal = normalize(mul((float3x3)g_worldTransform, input.normal)); - result.eyeToPoint = normalize(input.pos.xyz - g_eyePos.xyz); + // transform position into light coordinate frame (world) + float3 pos = mul(g_worldTransform, float4(input.pos, 1.0f)).xyz; - // rotate eye direction into light coordinate frame - result.eyeToPoint = mul(result.eyeToPoint, (float3x3)g_worldTransform); + // direction from eye to pos + result.eyeToPoint = normalize(pos - g_eyePos.xyz); result.tex = input.tex; return result; diff --git a/src/winMain.cpp b/src/winMain.cpp index 77e47b7..a30fb51 100644 --- a/src/winMain.cpp +++ b/src/winMain.cpp @@ -130,6 +130,7 @@ void AdjustArguments(CommandLineArgs& out_args) std::wstringstream caption; caption << "NOT FOUND: -mediaDir " << out_args.m_mediaDir; MessageBox(0, caption.str().c_str(), L"ERROR", MB_OK); + exit(-1); } } } @@ -256,17 +257,17 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case 'E': g_keyState.key.right = 0; break; - case 'Z': + case 'F': g_keyState.key.up = 0; break; - case 'C': + case 'V': g_keyState.key.down = 0; break; - case 'V': + case 'Z': g_keyState.key.rotzl = 0; break; - case 'B': + case 'C': g_keyState.key.rotzr = 0; break; @@ -306,17 +307,17 @@ LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) case 'E': g_keyState.key.right = 1; break; - case 'Z': + case 'F': g_keyState.key.up = 1; break; - case 'C': + case 'V': g_keyState.key.down = 1; break; - case 'V': + case 'Z': g_keyState.key.rotzl = 1; break; - case 'B': + case 'C': g_keyState.key.rotzr = 1; break;