From bf0f20df9b24e2147e60415ad3ebd6e6c23c13ab Mon Sep 17 00:00:00 2001 From: Triang3l Date: Fri, 17 Aug 2018 16:55:31 +0300 Subject: [PATCH] [D3D12] Immediate drawer --- src/xenia/ui/d3d12/d3d12_context.cc | 4 + src/xenia/ui/d3d12/d3d12_immediate_drawer.cc | 542 ++++++++++++++++++- src/xenia/ui/d3d12/d3d12_immediate_drawer.h | 30 +- src/xenia/ui/d3d12/shaders/immediate.ps.hlsl | 16 +- src/xenia/ui/d3d12/shaders/immediate.vs.hlsl | 24 +- 5 files changed, 567 insertions(+), 49 deletions(-) diff --git a/src/xenia/ui/d3d12/d3d12_context.cc b/src/xenia/ui/d3d12/d3d12_context.cc index 7fd78acbf..daa7badbb 100644 --- a/src/xenia/ui/d3d12/d3d12_context.cc +++ b/src/xenia/ui/d3d12/d3d12_context.cc @@ -256,6 +256,10 @@ void D3D12Context::BeginSwap() { barrier.Transition.StateBefore = D3D12_RESOURCE_STATE_PRESENT; barrier.Transition.StateAfter = D3D12_RESOURCE_STATE_RENDER_TARGET; graphics_command_list->ResourceBarrier(1, &barrier); + + D3D12_CPU_DESCRIPTOR_HANDLE back_buffer_rtv = GetSwapChainBackBufferRTV(); + graphics_command_list->OMSetRenderTargets(1, &back_buffer_rtv, TRUE, + nullptr); } } diff --git a/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc b/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc index fb663eea6..fe879068a 100644 --- a/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc +++ b/src/xenia/ui/d3d12/d3d12_immediate_drawer.cc @@ -9,7 +9,11 @@ #include "xenia/ui/d3d12/d3d12_immediate_drawer.h" +#include + +#include "xenia/base/assert.h" #include "xenia/base/logging.h" +#include "xenia/base/math.h" namespace xe { namespace ui { @@ -21,10 +25,87 @@ namespace d3d12 { class D3D12ImmediateTexture : public ImmediateTexture { public: - D3D12ImmediateTexture(uint32_t width, uint32_t height) - : ImmediateTexture(width, height) {} + static constexpr DXGI_FORMAT kFormat = DXGI_FORMAT_R8G8B8A8_UNORM; + + D3D12ImmediateTexture(uint32_t width, uint32_t height, + ImmediateTextureFilter filter, bool repeat); + + bool Initialize(ID3D12Device* device); + void Shutdown(); + + ID3D12Resource* GetResource() const { return resource_; } + void Transition(D3D12_RESOURCE_STATES new_state, + ID3D12GraphicsCommandList* command_list); + + ImmediateTextureFilter GetFilter() const { return filter_; } + bool IsRepeated() const { return repeat_; } + + private: + ID3D12Resource* resource_ = nullptr; + D3D12_RESOURCE_STATES state_; + ImmediateTextureFilter filter_; + bool repeat_; }; +D3D12ImmediateTexture::D3D12ImmediateTexture(uint32_t width, uint32_t height, + ImmediateTextureFilter filter, + bool repeat) + : ImmediateTexture(width, height), filter_(filter), repeat_(repeat) { + handle = reinterpret_cast(this); +} + +bool D3D12ImmediateTexture::Initialize(ID3D12Device* device) { + // The first operation will likely be copying the contents. + state_ = D3D12_RESOURCE_STATE_COPY_DEST; + + D3D12_HEAP_PROPERTIES heap_properties = {}; + heap_properties.Type = D3D12_HEAP_TYPE_DEFAULT; + D3D12_RESOURCE_DESC resource_desc; + resource_desc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D; + resource_desc.Alignment = 0; + resource_desc.Width = width; + resource_desc.Height = height; + resource_desc.DepthOrArraySize = 1; + resource_desc.MipLevels = 1; + resource_desc.Format = kFormat; + resource_desc.SampleDesc.Count = 1; + resource_desc.SampleDesc.Quality = 0; + resource_desc.Layout = D3D12_TEXTURE_LAYOUT_UNKNOWN; + resource_desc.Flags = D3D12_RESOURCE_FLAG_NONE; + if (FAILED(device->CreateCommittedResource( + &heap_properties, D3D12_HEAP_FLAG_NONE, &resource_desc, state_, + nullptr, IID_PPV_ARGS(&resource_)))) { + XELOGE("Failed to create a %ux%u texture for immediate drawing", width, + height); + return false; + } + + return true; +} + +void D3D12ImmediateTexture::Shutdown() { + if (resource_ != nullptr) { + resource_->Release(); + resource_ = nullptr; + } +} + +void D3D12ImmediateTexture::Transition( + D3D12_RESOURCE_STATES new_state, ID3D12GraphicsCommandList* command_list) { + if (resource_ == nullptr || state_ == new_state) { + return; + } + D3D12_RESOURCE_BARRIER barrier; + barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION; + barrier.Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE; + barrier.Transition.pResource = resource_; + barrier.Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES; + barrier.Transition.StateBefore = state_; + barrier.Transition.StateAfter = new_state; + command_list->ResourceBarrier(1, &barrier); + state_ = new_state; +} + D3D12ImmediateDrawer::D3D12ImmediateDrawer(D3D12Context* graphics_context) : ImmediateDrawer(graphics_context), context_(graphics_context) {} @@ -37,6 +118,15 @@ bool D3D12ImmediateDrawer::Initialize() { // Create the root signature. D3D12_ROOT_PARAMETER root_parameters[size_t(RootParameter::kCount)]; D3D12_DESCRIPTOR_RANGE descriptor_range_texture, descriptor_range_sampler; + { + auto& root_parameter = + root_parameters[size_t(RootParameter::kRestrictTextureSamples)]; + root_parameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; + root_parameter.Constants.ShaderRegister = 0; + root_parameter.Constants.RegisterSpace = 0; + root_parameter.Constants.Num32BitValues = 1; + root_parameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; + } { auto& root_parameter = root_parameters[size_t(RootParameter::kTexture)]; root_parameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE; @@ -63,15 +153,6 @@ bool D3D12ImmediateDrawer::Initialize() { descriptor_range_sampler.OffsetInDescriptorsFromTableStart = 0; root_parameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; } - { - auto& root_parameter = - root_parameters[size_t(RootParameter::kRestrictTextureSamples)]; - root_parameter.ParameterType = D3D12_ROOT_PARAMETER_TYPE_32BIT_CONSTANTS; - root_parameter.Constants.ShaderRegister = 0; - root_parameter.Constants.RegisterSpace = 0; - root_parameter.Constants.Num32BitValues = 1; - root_parameter.ShaderVisibility = D3D12_SHADER_VISIBILITY_PIXEL; - } { auto& root_parameter = root_parameters[size_t(RootParameter::kViewportInvSize)]; @@ -89,13 +170,22 @@ bool D3D12ImmediateDrawer::Initialize() { root_signature_desc.Flags = D3D12_ROOT_SIGNATURE_FLAG_ALLOW_INPUT_ASSEMBLER_INPUT_LAYOUT; ID3DBlob* root_signature_blob; - if (FAILED(D3D12SerializeRootSignature(&root_signature_desc, - D3D_ROOT_SIGNATURE_VERSION_1, - &root_signature_blob, nullptr))) { + ID3DBlob* root_signature_error_blob = nullptr; + if (FAILED(D3D12SerializeRootSignature( + &root_signature_desc, D3D_ROOT_SIGNATURE_VERSION_1, + &root_signature_blob, &root_signature_error_blob))) { XELOGE("Failed to serialize immediate drawer root signature"); + if (root_signature_error_blob != nullptr) { + XELOGE("%s", reinterpret_cast( + root_signature_error_blob->GetBufferPointer())); + root_signature_error_blob->Release(); + } Shutdown(); return false; } + if (root_signature_error_blob != nullptr) { + root_signature_error_blob->Release(); + } if (FAILED(device->CreateRootSignature( 0, root_signature_blob->GetBufferPointer(), root_signature_blob->GetBufferSize(), @@ -107,6 +197,58 @@ bool D3D12ImmediateDrawer::Initialize() { } root_signature_blob->Release(); + // Create the pipelines. + D3D12_GRAPHICS_PIPELINE_STATE_DESC pipeline_desc = {}; + pipeline_desc.pRootSignature = root_signature_; + pipeline_desc.VS.pShaderBytecode = immediate_vs; + pipeline_desc.VS.BytecodeLength = sizeof(immediate_vs); + pipeline_desc.PS.pShaderBytecode = immediate_ps; + pipeline_desc.PS.BytecodeLength = sizeof(immediate_ps); + D3D12_RENDER_TARGET_BLEND_DESC& pipeline_blend_desc = + pipeline_desc.BlendState.RenderTarget[0]; + pipeline_blend_desc.BlendEnable = TRUE; + pipeline_blend_desc.SrcBlend = D3D12_BLEND_SRC_ALPHA; + pipeline_blend_desc.DestBlend = D3D12_BLEND_INV_SRC_ALPHA; + pipeline_blend_desc.BlendOp = D3D12_BLEND_OP_ADD; + pipeline_blend_desc.SrcBlendAlpha = D3D12_BLEND_SRC_ALPHA; + pipeline_blend_desc.DestBlendAlpha = D3D12_BLEND_INV_SRC_ALPHA; + pipeline_blend_desc.BlendOpAlpha = D3D12_BLEND_OP_ADD; + pipeline_blend_desc.RenderTargetWriteMask = D3D12_COLOR_WRITE_ENABLE_ALL; + pipeline_desc.SampleMask = UINT_MAX; + pipeline_desc.RasterizerState.FillMode = D3D12_FILL_MODE_SOLID; + pipeline_desc.RasterizerState.CullMode = D3D12_CULL_MODE_BACK; + pipeline_desc.RasterizerState.DepthClipEnable = TRUE; + D3D12_INPUT_ELEMENT_DESC pipeline_input_elements[3] = {}; + pipeline_input_elements[0].SemanticName = "POSITION"; + pipeline_input_elements[0].Format = DXGI_FORMAT_R32G32_FLOAT; + pipeline_input_elements[0].AlignedByteOffset = 0; + pipeline_input_elements[1].SemanticName = "TEXCOORD"; + pipeline_input_elements[1].Format = DXGI_FORMAT_R32G32_FLOAT; + pipeline_input_elements[1].AlignedByteOffset = 8; + pipeline_input_elements[2].SemanticName = "COLOR"; + pipeline_input_elements[2].Format = DXGI_FORMAT_R8G8B8A8_UNORM; + pipeline_input_elements[2].AlignedByteOffset = 16; + pipeline_desc.InputLayout.pInputElementDescs = pipeline_input_elements; + pipeline_desc.InputLayout.NumElements = + UINT(xe::countof(pipeline_input_elements)); + pipeline_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_TRIANGLE; + pipeline_desc.NumRenderTargets = 1; + pipeline_desc.RTVFormats[0] = D3D12Context::kSwapChainFormat; + pipeline_desc.SampleDesc.Count = 1; + if (FAILED(device->CreateGraphicsPipelineState( + &pipeline_desc, IID_PPV_ARGS(&pipeline_triangle_)))) { + XELOGE("Failed to create immediate drawer triangle pipeline state"); + Shutdown(); + return false; + } + pipeline_desc.PrimitiveTopologyType = D3D12_PRIMITIVE_TOPOLOGY_TYPE_LINE; + if (FAILED(device->CreateGraphicsPipelineState( + &pipeline_desc, IID_PPV_ARGS(&pipeline_line_)))) { + XELOGE("Failed to create immediate drawer line pipeline state"); + Shutdown(); + return false; + } + // Create the samplers. D3D12_DESCRIPTOR_HEAP_DESC sampler_heap_desc; sampler_heap_desc.Type = D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER; @@ -160,6 +302,12 @@ bool D3D12ImmediateDrawer::Initialize() { uint32_t(SamplerIndex::kLinearRepeat) * sampler_size; device->CreateSampler(&sampler_desc, sampler_handle); + // Create pools for draws. + vertex_buffer_pool_ = + std::make_unique(context_, 2 * 1024 * 1024); + texture_descriptor_pool_ = std::make_unique( + context_, D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 2048); + // Reset the current state. current_command_list_ = nullptr; @@ -168,15 +316,32 @@ bool D3D12ImmediateDrawer::Initialize() { void D3D12ImmediateDrawer::Shutdown() { for (auto& texture_upload : texture_uploads_submitted_) { - texture_upload.data_resource->Release(); + texture_upload.buffer->Release(); } texture_uploads_submitted_.clear(); + for (auto& texture_upload : texture_uploads_pending_) { + texture_upload.buffer->Release(); + } + texture_uploads_pending_.clear(); + + texture_descriptor_pool_.reset(); + vertex_buffer_pool_.reset(); + if (sampler_heap_ != nullptr) { sampler_heap_->Release(); sampler_heap_ = nullptr; } + if (pipeline_line_ != nullptr) { + pipeline_line_->Release(); + pipeline_line_ = nullptr; + } + if (pipeline_triangle_ != nullptr) { + pipeline_triangle_->Release(); + pipeline_triangle_ = nullptr; + } + if (root_signature_ != nullptr) { root_signature_->Release(); root_signature_ = nullptr; @@ -186,22 +351,112 @@ void D3D12ImmediateDrawer::Shutdown() { std::unique_ptr D3D12ImmediateDrawer::CreateTexture( uint32_t width, uint32_t height, ImmediateTextureFilter filter, bool repeat, const uint8_t* data) { - // TODO(Triang3l): Implement CreateTexture. - auto texture = std::make_unique(width, height); + auto texture = + std::make_unique(width, height, filter, repeat); + texture->Initialize(context_->GetD3D12Provider()->GetDevice()); + if (data != nullptr) { + UpdateTexture(texture.get(), data); + } return std::unique_ptr(texture.release()); } void D3D12ImmediateDrawer::UpdateTexture(ImmediateTexture* texture, const uint8_t* data) { - // TODO(Triang3l): Implement UpdateTexture. + D3D12ImmediateTexture* d3d_texture = + static_cast(texture); + ID3D12Resource* texture_resource = d3d_texture->GetResource(); + if (texture_resource == nullptr) { + return; + } + uint32_t width = d3d_texture->width, height = d3d_texture->height; + + auto device = context_->GetD3D12Provider()->GetDevice(); + + // Create and fill the upload buffer. + D3D12_RESOURCE_DESC texture_desc = texture_resource->GetDesc(); + D3D12_PLACED_SUBRESOURCE_FOOTPRINT upload_footprint; + UINT64 upload_size; + device->GetCopyableFootprints(&texture_desc, 0, 1, 0, &upload_footprint, + nullptr, nullptr, &upload_size); + D3D12_HEAP_PROPERTIES heap_properties = {}; + heap_properties.Type = D3D12_HEAP_TYPE_UPLOAD; + D3D12_RESOURCE_DESC buffer_desc; + buffer_desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; + buffer_desc.Alignment = 0; + buffer_desc.Width = upload_size; + buffer_desc.Height = 1; + buffer_desc.DepthOrArraySize = 1; + buffer_desc.MipLevels = 1; + buffer_desc.Format = DXGI_FORMAT_UNKNOWN; + buffer_desc.SampleDesc.Count = 1; + buffer_desc.SampleDesc.Quality = 0; + buffer_desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR; + buffer_desc.Flags = D3D12_RESOURCE_FLAG_NONE; + ID3D12Resource* buffer; + if (FAILED(device->CreateCommittedResource( + &heap_properties, D3D12_HEAP_FLAG_NONE, &buffer_desc, + D3D12_RESOURCE_STATE_GENERIC_READ, nullptr, IID_PPV_ARGS(&buffer)))) { + XELOGE( + "Failed to create an upload buffer for a %ux%u texture for " + "immediate drawing", + width, height); + return; + } + D3D12_RANGE buffer_read_range; + buffer_read_range.Begin = 0; + buffer_read_range.End = 0; + void* buffer_mapping; + if (FAILED(buffer->Map(0, &buffer_read_range, &buffer_mapping))) { + XELOGE( + "Failed to map an upload buffer for a %ux%u texture for immediate " + "drawing", + width, height); + buffer->Release(); + return; + } + uint8_t* buffer_row = + reinterpret_cast(buffer_mapping) + upload_footprint.Offset; + for (uint32_t i = 0; i < height; ++i) { + std::memcpy(buffer_row, data, width * 4); + data += width * 4; + buffer_row += upload_footprint.Footprint.RowPitch; + } + buffer->Unmap(0, nullptr); + + if (current_command_list_ != nullptr) { + // Upload the texture right now if we can. + d3d_texture->Transition(D3D12_RESOURCE_STATE_COPY_DEST, + current_command_list_); + D3D12_TEXTURE_COPY_LOCATION location_source, location_dest; + location_source.pResource = buffer; + location_source.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + location_source.PlacedFootprint = upload_footprint; + location_dest.pResource = texture_resource; + location_dest.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + location_dest.SubresourceIndex = 0; + current_command_list_->CopyTextureRegion(&location_dest, 0, 0, 0, + &location_source, nullptr); + SubmittedTextureUpload submitted_upload; + submitted_upload.buffer = buffer; + submitted_upload.frame = context_->GetCurrentFrame(); + texture_uploads_submitted_.push_back(submitted_upload); + } else { + // Defer uploading to the next frame when there's a command list. + PendingTextureUpload pending_upload; + pending_upload.texture = texture; + pending_upload.buffer = buffer; + texture_uploads_pending_.push_back(pending_upload); + } } void D3D12ImmediateDrawer::Begin(int render_target_width, int render_target_height) { + auto device = context_->GetD3D12Provider()->GetDevice(); + // Use the compositing command list. current_command_list_ = context_->GetSwapCommandList(); - uint32_t queue_frame = context_->GetCurrentQueueFrame(); + uint64_t current_frame = context_->GetCurrentFrame(); uint64_t last_completed_frame = context_->GetLastCompletedFrame(); // Remove temporary buffers for completed texture uploads. @@ -212,26 +467,261 @@ void D3D12ImmediateDrawer::Begin(int render_target_width, ++erase_uploads_end; break; } - erase_uploads_end->data_resource->Release(); + erase_uploads_end->buffer->Release(); ++erase_uploads_end; } texture_uploads_submitted_.erase(texture_uploads_submitted_.begin(), erase_uploads_end); + + // Submit texture updates that happened between frames. + while (!texture_uploads_pending_.empty()) { + const PendingTextureUpload& pending_upload = + texture_uploads_pending_.back(); + D3D12ImmediateTexture* texture = + static_cast(pending_upload.texture); + texture->Transition(D3D12_RESOURCE_STATE_COPY_DEST, current_command_list_); + ID3D12Resource* texture_resource = texture->GetResource(); + D3D12_RESOURCE_DESC texture_desc = texture_resource->GetDesc(); + D3D12_TEXTURE_COPY_LOCATION location_source, location_dest; + location_source.pResource = pending_upload.buffer; + location_source.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; + device->GetCopyableFootprints(&texture_desc, 0, 1, 0, + &location_source.PlacedFootprint, nullptr, + nullptr, nullptr); + location_dest.pResource = texture_resource; + location_dest.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX; + location_dest.SubresourceIndex = 0; + current_command_list_->CopyTextureRegion(&location_dest, 0, 0, 0, + &location_source, nullptr); + SubmittedTextureUpload submitted_upload; + submitted_upload.buffer = pending_upload.buffer; + submitted_upload.frame = current_frame; + texture_uploads_submitted_.push_back(submitted_upload); + texture_uploads_pending_.pop_back(); + } + + vertex_buffer_pool_->BeginFrame(); + texture_descriptor_pool_->BeginFrame(); + texture_descriptor_pool_full_update_ = 0; + + current_render_target_width_ = render_target_width; + current_render_target_height_ = render_target_height; + D3D12_VIEWPORT viewport; + viewport.TopLeftX = 0.0f; + viewport.TopLeftY = 0.0f; + viewport.Width = float(render_target_width); + viewport.Height = float(render_target_height); + viewport.MinDepth = 0.0f; + viewport.MaxDepth = 1.0f; + current_command_list_->RSSetViewports(1, &viewport); + + current_command_list_->SetGraphicsRootSignature(root_signature_); + float viewport_inv_scale[2]; + viewport_inv_scale[0] = 1.0f / viewport.Width; + viewport_inv_scale[1] = 1.0f / viewport.Height; + current_command_list_->SetGraphicsRoot32BitConstants( + UINT(RootParameter::kViewportInvSize), 2, viewport_inv_scale, 0); } void D3D12ImmediateDrawer::BeginDrawBatch(const ImmediateDrawBatch& batch) { - // TODO(Triang3l): Implement BeginDrawBatch. + assert_not_null(current_command_list_); + if (current_command_list_ == nullptr) { + return; + } + + batch_open_ = false; + + // Bind the vertices. + D3D12_VERTEX_BUFFER_VIEW vertex_buffer_view; + vertex_buffer_view.StrideInBytes = UINT(sizeof(ImmediateVertex)); + vertex_buffer_view.SizeInBytes = + batch.vertex_count * uint32_t(sizeof(ImmediateVertex)); + void* vertex_buffer_mapping = vertex_buffer_pool_->RequestFull( + vertex_buffer_view.SizeInBytes, nullptr, nullptr, + &vertex_buffer_view.BufferLocation); + if (vertex_buffer_mapping == nullptr) { + XELOGE("Failed to get a buffer for %u vertices in the immediate drawer", + batch.vertex_count); + return; + } + std::memcpy(vertex_buffer_mapping, batch.vertices, + vertex_buffer_view.SizeInBytes); + current_command_list_->IASetVertexBuffers(0, 1, &vertex_buffer_view); + + // Bind the indices. + batch_has_index_buffer_ = batch.indices != nullptr; + if (batch_has_index_buffer_) { + D3D12_INDEX_BUFFER_VIEW index_buffer_view; + index_buffer_view.SizeInBytes = batch.index_count * sizeof(uint16_t); + index_buffer_view.Format = DXGI_FORMAT_R16_UINT; + void* index_buffer_mapping = vertex_buffer_pool_->RequestFull( + index_buffer_view.SizeInBytes, nullptr, nullptr, + &index_buffer_view.BufferLocation); + if (index_buffer_mapping == nullptr) { + XELOGE("Failed to get a buffer for %u indices in the immediate drawer", + batch.index_count); + return; + } + std::memcpy(index_buffer_mapping, batch.indices, + index_buffer_view.SizeInBytes); + current_command_list_->IASetIndexBuffer(&index_buffer_view); + } + + batch_open_ = true; + current_primitive_topology_ = D3D_PRIMITIVE_TOPOLOGY_UNDEFINED; + current_texture_ = nullptr; + current_sampler_index_ = SamplerIndex::kInvalid; } void D3D12ImmediateDrawer::Draw(const ImmediateDraw& draw) { - // TODO(Triang3l): Implement Draw. + assert_not_null(current_command_list_); + if (current_command_list_ == nullptr) { + return; + } + + if (!batch_open_) { + // Could be an error while obtaining the vertex and index buffers. + return; + } + + auto provider = context_->GetD3D12Provider(); + auto device = provider->GetDevice(); + + // Bind the texture. + auto texture = reinterpret_cast(draw.texture_handle); + ID3D12Resource* texture_resource; + if (texture != nullptr) { + texture->Transition(D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE, + current_command_list_); + texture_resource = texture->GetResource(); + } else { + texture_resource = nullptr; + } + bool bind_texture = current_texture_ != texture; + uint32_t texture_descriptor_index; + uint64_t texture_full_update = texture_descriptor_pool_->Request( + texture_descriptor_pool_full_update_, bind_texture ? 1 : 0, 1, + texture_descriptor_index); + if (texture_full_update == 0) { + return; + } + if (texture_descriptor_pool_full_update_ != texture_full_update) { + bind_texture = true; + texture_descriptor_pool_full_update_ = texture_full_update; + ID3D12DescriptorHeap* descriptor_heaps[] = { + texture_descriptor_pool_->GetLastRequestHeap(), sampler_heap_}; + current_command_list_->SetDescriptorHeaps(2, descriptor_heaps); + } + if (bind_texture) { + auto descriptor_size_view = provider->GetDescriptorSizeView(); + D3D12_SHADER_RESOURCE_VIEW_DESC texture_view_desc; + texture_view_desc.Format = D3D12ImmediateTexture::kFormat; + texture_view_desc.ViewDimension = D3D12_SRV_DIMENSION_TEXTURE2D; + texture_view_desc.Shader4ComponentMapping = + D3D12_DEFAULT_SHADER_4_COMPONENT_MAPPING; + texture_view_desc.Texture2D.MostDetailedMip = 0; + texture_view_desc.Texture2D.MipLevels = 1; + texture_view_desc.Texture2D.PlaneSlice = 0; + texture_view_desc.Texture2D.ResourceMinLODClamp = 0.0f; + D3D12_CPU_DESCRIPTOR_HANDLE texture_view_cpu_handle; + texture_view_cpu_handle.ptr = + texture_descriptor_pool_->GetLastRequestHeapCPUStart().ptr + + texture_descriptor_index * descriptor_size_view; + device->CreateShaderResourceView(texture_resource, &texture_view_desc, + texture_view_cpu_handle); + D3D12_GPU_DESCRIPTOR_HANDLE texture_view_gpu_handle; + texture_view_gpu_handle.ptr = + texture_descriptor_pool_->GetLastRequestHeapGPUStart().ptr + + texture_descriptor_index * descriptor_size_view; + current_command_list_->SetGraphicsRootDescriptorTable( + UINT(RootParameter::kTexture), texture_view_gpu_handle); + current_texture_ = texture; + } + + // Bind the sampler. + SamplerIndex sampler_index; + if (texture != nullptr) { + if (texture->GetFilter() == ImmediateTextureFilter::kLinear) { + sampler_index = texture->IsRepeated() ? SamplerIndex::kLinearRepeat + : SamplerIndex::kLinearClamp; + } else { + sampler_index = texture->IsRepeated() ? SamplerIndex::kNearestRepeat + : SamplerIndex::kNearestClamp; + } + } else { + sampler_index = SamplerIndex::kNearestClamp; + } + if (current_sampler_index_ != sampler_index) { + D3D12_GPU_DESCRIPTOR_HANDLE sampler_gpu_handle; + sampler_gpu_handle.ptr = + sampler_heap_gpu_start_.ptr + + UINT(sampler_index) * provider->GetDescriptorSizeSampler(); + current_command_list_->SetGraphicsRootDescriptorTable( + UINT(RootParameter::kSampler), sampler_gpu_handle); + current_sampler_index_ = sampler_index; + } + + // Set whether texture coordinates need to be restricted. + uint32_t restrict_texture_samples = draw.restrict_texture_samples ? 1 : 0; + current_command_list_->SetGraphicsRoot32BitConstants( + UINT(RootParameter::kRestrictTextureSamples), 1, + &restrict_texture_samples, 0); + + // Set the primitive type and the pipeline for it. + D3D_PRIMITIVE_TOPOLOGY primitive_topology; + ID3D12PipelineState* pipeline; + switch (draw.primitive_type) { + case ImmediatePrimitiveType::kLines: + primitive_topology = D3D_PRIMITIVE_TOPOLOGY_LINELIST; + pipeline = pipeline_line_; + break; + case ImmediatePrimitiveType::kTriangles: + primitive_topology = D3D_PRIMITIVE_TOPOLOGY_TRIANGLELIST; + pipeline = pipeline_triangle_; + break; + default: + assert_unhandled_case(draw.primitive_type); + return; + } + if (current_primitive_topology_ != primitive_topology) { + current_command_list_->IASetPrimitiveTopology(primitive_topology); + current_command_list_->SetPipelineState(pipeline); + current_primitive_topology_ = primitive_topology; + } + + // Set the scissor rectangle if enabled. + D3D12_RECT scissor; + if (draw.scissor) { + scissor.left = draw.scissor_rect[0]; + scissor.top = current_render_target_height_ - + (draw.scissor_rect[1] + draw.scissor_rect[3]); + scissor.right = scissor.left + draw.scissor_rect[2]; + scissor.bottom = scissor.top + draw.scissor_rect[3]; + } else { + scissor.left = 0; + scissor.top = 0; + scissor.right = current_render_target_width_; + scissor.bottom = current_render_target_height_; + } + current_command_list_->RSSetScissorRects(1, &scissor); + + // Draw. + if (batch_has_index_buffer_) { + current_command_list_->DrawIndexedInstanced( + draw.count, 1, draw.index_offset, draw.base_vertex, 0); + } else { + current_command_list_->DrawInstanced(draw.count, 1, draw.base_vertex, 0); + } } -void D3D12ImmediateDrawer::EndDrawBatch() { - // TODO(Triang3l): Implement EndDrawBatch. -} +void D3D12ImmediateDrawer::EndDrawBatch() { batch_open_ = false; } -void D3D12ImmediateDrawer::End() { current_command_list_ = nullptr; } +void D3D12ImmediateDrawer::End() { + texture_descriptor_pool_->EndFrame(); + vertex_buffer_pool_->EndFrame(); + + current_command_list_ = nullptr; +} } // namespace d3d12 } // namespace ui diff --git a/src/xenia/ui/d3d12/d3d12_immediate_drawer.h b/src/xenia/ui/d3d12/d3d12_immediate_drawer.h index 6fc81007c..64a32a652 100644 --- a/src/xenia/ui/d3d12/d3d12_immediate_drawer.h +++ b/src/xenia/ui/d3d12/d3d12_immediate_drawer.h @@ -12,10 +12,12 @@ #include #include +#include #include "xenia/ui/d3d12/command_list.h" #include "xenia/ui/d3d12/d3d12_api.h" #include "xenia/ui/d3d12/d3d12_context.h" +#include "xenia/ui/d3d12/pools.h" #include "xenia/ui/immediate_drawer.h" namespace xe { @@ -48,33 +50,53 @@ class D3D12ImmediateDrawer : public ImmediateDrawer { ID3D12RootSignature* root_signature_ = nullptr; enum class RootParameter { + kRestrictTextureSamples, kTexture, kSampler, - kRestrictTextureSamples, kViewportInvSize, kCount }; + ID3D12PipelineState* pipeline_triangle_ = nullptr; + ID3D12PipelineState* pipeline_line_ = nullptr; + enum class SamplerIndex { kNearestClamp, kLinearClamp, kNearestRepeat, kLinearRepeat, - kCount + kCount, + kInvalid = kCount }; ID3D12DescriptorHeap* sampler_heap_ = nullptr; D3D12_CPU_DESCRIPTOR_HANDLE sampler_heap_cpu_start_; D3D12_GPU_DESCRIPTOR_HANDLE sampler_heap_gpu_start_; - ID3D12GraphicsCommandList* current_command_list_ = nullptr; + std::unique_ptr vertex_buffer_pool_ = nullptr; + std::unique_ptr texture_descriptor_pool_ = nullptr; + + struct PendingTextureUpload { + ImmediateTexture* texture; + ID3D12Resource* buffer; + }; + std::vector texture_uploads_pending_; struct SubmittedTextureUpload { - ID3D12Resource* data_resource; + ID3D12Resource* buffer; uint64_t frame; }; std::deque texture_uploads_submitted_; + + ID3D12GraphicsCommandList* current_command_list_ = nullptr; + int current_render_target_width_, current_render_target_height_; + bool batch_open_ = false; + bool batch_has_index_buffer_; + uint64_t texture_descriptor_pool_full_update_; + D3D_PRIMITIVE_TOPOLOGY current_primitive_topology_; + ImmediateTexture* current_texture_; + SamplerIndex current_sampler_index_; }; } // namespace d3d12 diff --git a/src/xenia/ui/d3d12/shaders/immediate.ps.hlsl b/src/xenia/ui/d3d12/shaders/immediate.ps.hlsl index 6a0cd8566..3d9c8de51 100644 --- a/src/xenia/ui/d3d12/shaders/immediate.ps.hlsl +++ b/src/xenia/ui/d3d12/shaders/immediate.ps.hlsl @@ -1,16 +1,16 @@ -Texture2D immediate_texture : register(t0); -SamplerState immediate_sampler : register(s0); -bool restrict_texture_samples : register(b0); +Texture2D xe_immediate_texture : register(t0); +SamplerState xe_immediate_sampler : register(s0); +bool xe_restrict_texture_samples : register(b0); -struct ps_input { - float2 uv : TEXCOORD0; +struct XePixelShaderInput { + float2 texcoord : TEXCOORD0; float4 color : TEXCOORD1; }; -float4 main(ps_input input) : SV_Target { +float4 main(XePixelShaderInput input) : SV_Target { float4 output = input.color; - if (!restrict_texture_samples || input.uv.x <= 1.0) { - output *= immediate_texture.Sample(immediate_sampler, input.uv); + if (!xe_restrict_texture_samples || input.texcoord.x <= 1.0) { + output *= xe_immediate_texture.Sample(xe_immediate_sampler, input.texcoord); } return output; } diff --git a/src/xenia/ui/d3d12/shaders/immediate.vs.hlsl b/src/xenia/ui/d3d12/shaders/immediate.vs.hlsl index 75f823464..7c3e22530 100644 --- a/src/xenia/ui/d3d12/shaders/immediate.vs.hlsl +++ b/src/xenia/ui/d3d12/shaders/immediate.vs.hlsl @@ -1,21 +1,23 @@ -float2 viewport_inv_size : register(b0); +float2 xe_viewport_inv_size : register(b0); -struct vs_input { - float2 pos : POSITION; - float2 uv : TEXCOORD; +struct XeVertexShaderInput { + float2 position : POSITION; + float2 texcoord : TEXCOORD; float4 color : COLOR; }; -struct vs_output { - float4 pos : SV_Position; - float2 uv : TEXCOORD0; +struct XeVertexShaderOutput { + float2 texcoord : TEXCOORD0; float4 color : TEXCOORD1; + float4 position : SV_Position; }; -vs_output main(vs_input input) { - vs_output output; - output.pos = float4(viewport_inv_size * input.pos, 0.0, 1.0); - output.uv = input.uv; +XeVertexShaderOutput main(XeVertexShaderInput input) { + XeVertexShaderOutput output; + output.position = float4( + input.position * xe_viewport_inv_size * float2(2.0, -2.0) + + float2(-1.0, 1.0), 0.0, 1.0); + output.texcoord = input.texcoord; output.color = input.color; return output; }