[D3D12] Shaders (not all compiling yet)
This commit is contained in:
@@ -9,6 +9,12 @@
|
||||
|
||||
#include "xenia/gpu/d3d12/d3d12_command_processor.h"
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/base/profiling.h"
|
||||
#include "xenia/gpu/d3d12/d3d12_shader.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
namespace d3d12 {
|
||||
@@ -18,33 +24,133 @@ D3D12CommandProcessor::D3D12CommandProcessor(
|
||||
: CommandProcessor(graphics_system, kernel_state) {}
|
||||
D3D12CommandProcessor::~D3D12CommandProcessor() = default;
|
||||
|
||||
void D3D12CommandProcessor::ClearCaches() {
|
||||
CommandProcessor::ClearCaches();
|
||||
cache_clear_requested_ = true;
|
||||
}
|
||||
|
||||
bool D3D12CommandProcessor::SetupContext() {
|
||||
return CommandProcessor::SetupContext();
|
||||
if (!CommandProcessor::SetupContext()) {
|
||||
XELOGE("Unable to initialize base command processor context");
|
||||
return false;
|
||||
}
|
||||
|
||||
auto context = GetD3D12Context();
|
||||
|
||||
pipeline_cache_ = std::make_unique<PipelineCache>(register_file_, context);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void D3D12CommandProcessor::ShutdownContext() {
|
||||
return CommandProcessor::ShutdownContext();
|
||||
auto context = GetD3D12Context();
|
||||
context->AwaitAllFramesCompletion();
|
||||
|
||||
pipeline_cache_.reset();
|
||||
|
||||
CommandProcessor::ShutdownContext();
|
||||
}
|
||||
|
||||
void D3D12CommandProcessor::PerformSwap(uint32_t frontbuffer_ptr,
|
||||
uint32_t frontbuffer_width,
|
||||
uint32_t frontbuffer_height) {}
|
||||
uint32_t frontbuffer_height) {
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
|
||||
if (current_queue_frame_ != UINT32_MAX) {
|
||||
EndFrame();
|
||||
}
|
||||
|
||||
if (cache_clear_requested_) {
|
||||
cache_clear_requested_ = false;
|
||||
GetD3D12Context()->AwaitAllFramesCompletion();
|
||||
pipeline_cache_->ClearCache();
|
||||
}
|
||||
}
|
||||
|
||||
Shader* D3D12CommandProcessor::LoadShader(ShaderType shader_type,
|
||||
uint32_t guest_address,
|
||||
const uint32_t* host_address,
|
||||
uint32_t dword_count) {
|
||||
return nullptr;
|
||||
return pipeline_cache_->LoadShader(shader_type, guest_address, host_address,
|
||||
dword_count);
|
||||
}
|
||||
|
||||
bool D3D12CommandProcessor::IssueDraw(PrimitiveType prim_type,
|
||||
bool D3D12CommandProcessor::IssueDraw(PrimitiveType primitive_type,
|
||||
uint32_t index_count,
|
||||
IndexBufferInfo* index_buffer_info) {
|
||||
auto& regs = *register_file_;
|
||||
|
||||
#if FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // FINE_GRAINED_DRAW_SCOPES
|
||||
|
||||
auto enable_mode = static_cast<xenos::ModeControl>(
|
||||
regs[XE_GPU_REG_RB_MODECONTROL].u32 & 0x7);
|
||||
if (enable_mode == xenos::ModeControl::kIgnore) {
|
||||
// Ignored.
|
||||
return true;
|
||||
} else if (enable_mode == xenos::ModeControl::kCopy) {
|
||||
// Special copy handling.
|
||||
return IssueCopy();
|
||||
}
|
||||
|
||||
if ((regs[XE_GPU_REG_RB_SURFACE_INFO].u32 & 0x3FFF) == 0) {
|
||||
// Doesn't actually draw.
|
||||
return true;
|
||||
}
|
||||
|
||||
// Shaders will have already been defined by previous loads.
|
||||
// We need them to do just about anything so validate here.
|
||||
auto vertex_shader = static_cast<D3D12Shader*>(active_vertex_shader());
|
||||
auto pixel_shader = static_cast<D3D12Shader*>(active_pixel_shader());
|
||||
if (!vertex_shader) {
|
||||
// Always need a vertex shader.
|
||||
return false;
|
||||
}
|
||||
// Depth-only mode doesn't need a pixel shader (we'll use a fake one).
|
||||
if (enable_mode == xenos::ModeControl::kDepth) {
|
||||
// Use a dummy pixel shader when required.
|
||||
pixel_shader = nullptr;
|
||||
} else if (!pixel_shader) {
|
||||
// Need a pixel shader in normal color mode.
|
||||
return true;
|
||||
}
|
||||
|
||||
bool full_update = false;
|
||||
if (current_queue_frame_ == UINT32_MAX) {
|
||||
BeginFrame();
|
||||
full_update = true;
|
||||
}
|
||||
|
||||
auto pipeline_status = pipeline_cache_->ConfigurePipeline(
|
||||
vertex_shader, pixel_shader, primitive_type);
|
||||
if (pipeline_status == PipelineCache::UpdateStatus::kError) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool D3D12CommandProcessor::IssueCopy() { return true; }
|
||||
|
||||
void D3D12CommandProcessor::BeginFrame() {
|
||||
assert_true(current_queue_frame_ == UINT32_MAX);
|
||||
auto context = GetD3D12Context();
|
||||
|
||||
context->BeginSwap();
|
||||
|
||||
current_queue_frame_ = context->GetCurrentQueueFrame();
|
||||
}
|
||||
|
||||
void D3D12CommandProcessor::EndFrame() {
|
||||
assert_true(current_queue_frame_ != UINT32_MAX);
|
||||
auto context = GetD3D12Context();
|
||||
|
||||
context->EndSwap();
|
||||
|
||||
current_queue_frame_ = UINT32_MAX;
|
||||
}
|
||||
|
||||
} // namespace d3d12
|
||||
} // namespace gpu
|
||||
} // namespace xe
|
||||
|
||||
@@ -10,10 +10,14 @@
|
||||
#ifndef XENIA_GPU_D3D12_D3D12_COMMAND_PROCESSOR_H_
|
||||
#define XENIA_GPU_D3D12_D3D12_COMMAND_PROCESSOR_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "xenia/gpu/command_processor.h"
|
||||
#include "xenia/gpu/d3d12/d3d12_graphics_system.h"
|
||||
#include "xenia/gpu/d3d12/pipeline_cache.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/kernel/kernel_state.h"
|
||||
#include "xenia/ui/d3d12/d3d12_context.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
@@ -25,7 +29,14 @@ class D3D12CommandProcessor : public CommandProcessor {
|
||||
kernel::KernelState* kernel_state);
|
||||
~D3D12CommandProcessor();
|
||||
|
||||
private:
|
||||
void ClearCaches() override;
|
||||
|
||||
// Needed by everything that owns transient objects.
|
||||
xe::ui::d3d12::D3D12Context* GetD3D12Context() const {
|
||||
return static_cast<xe::ui::d3d12::D3D12Context*>(context_.get());
|
||||
}
|
||||
|
||||
protected:
|
||||
bool SetupContext() override;
|
||||
void ShutdownContext() override;
|
||||
|
||||
@@ -36,9 +47,19 @@ class D3D12CommandProcessor : public CommandProcessor {
|
||||
const uint32_t* host_address,
|
||||
uint32_t dword_count) override;
|
||||
|
||||
bool IssueDraw(PrimitiveType prim_type, uint32_t index_count,
|
||||
bool IssueDraw(PrimitiveType primitive_type, uint32_t index_count,
|
||||
IndexBufferInfo* index_buffer_info) override;
|
||||
bool IssueCopy() override;
|
||||
|
||||
private:
|
||||
void BeginFrame();
|
||||
void EndFrame();
|
||||
|
||||
bool cache_clear_requested_ = false;
|
||||
|
||||
std::unique_ptr<PipelineCache> pipeline_cache_;
|
||||
|
||||
uint32_t current_queue_frame_ = UINT32_MAX;
|
||||
};
|
||||
|
||||
} // namespace d3d12
|
||||
|
||||
117
src/xenia/gpu/d3d12/d3d12_shader.cc
Normal file
117
src/xenia/gpu/d3d12/d3d12_shader.cc
Normal file
@@ -0,0 +1,117 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2018 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/gpu/d3d12/d3d12_shader.h"
|
||||
|
||||
#include <gflags/gflags.h>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/gpu/gpu_flags.h"
|
||||
|
||||
DEFINE_bool(d3d12_shader_disasm, true,
|
||||
"Disassemble translated shaders after compilation.");
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
namespace d3d12 {
|
||||
|
||||
D3D12Shader::D3D12Shader(ShaderType shader_type, uint64_t data_hash,
|
||||
const uint32_t* dword_ptr, uint32_t dword_count)
|
||||
: Shader(shader_type, data_hash, dword_ptr, dword_count) {}
|
||||
|
||||
D3D12Shader::~D3D12Shader() {
|
||||
if (blob_ != nullptr) {
|
||||
blob_->Release();
|
||||
}
|
||||
}
|
||||
|
||||
bool D3D12Shader::Prepare() {
|
||||
assert_null(blob_);
|
||||
assert_true(is_valid());
|
||||
|
||||
const char* target;
|
||||
switch (shader_type_) {
|
||||
case ShaderType::kVertex:
|
||||
target = "vs_5_1";
|
||||
break;
|
||||
case ShaderType::kPixel:
|
||||
target = "ps_5_1";
|
||||
break;
|
||||
default:
|
||||
assert_unhandled_case(shader_type_);
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO(Triang3l): Choose the appropriate optimization level based on compile
|
||||
// time and how invariance is handled in vertex shaders.
|
||||
ID3DBlob* error_blob = nullptr;
|
||||
bool compiled =
|
||||
SUCCEEDED(D3DCompile(translated_binary_.data(), translated_binary_.size(),
|
||||
nullptr, nullptr, nullptr, "main", target,
|
||||
D3DCOMPILE_OPTIMIZATION_LEVEL0, 0, &blob_,
|
||||
&error_blob));
|
||||
|
||||
if (!compiled) {
|
||||
XELOGE("%s shader %.16llX compilation failed!", target, ucode_data_hash());
|
||||
}
|
||||
if (error_blob != nullptr) {
|
||||
if (compiled) {
|
||||
XELOGW("%s shader %.16llX compiled with warnings!", target,
|
||||
ucode_data_hash());
|
||||
XELOGW("%s", reinterpret_cast<const char*>(error_blob->GetBufferPointer()));
|
||||
XELOGW("HLSL source:");
|
||||
// The buffer isn't terminated.
|
||||
translated_binary_.push_back(0);
|
||||
XELOGW("%s", reinterpret_cast<const char*>(translated_binary_.data()));
|
||||
translated_binary_.pop_back();
|
||||
} else {
|
||||
XELOGE("%s", reinterpret_cast<const char*>(error_blob->GetBufferPointer()));
|
||||
XELOGE("HLSL source:");
|
||||
translated_binary_.push_back(0);
|
||||
XELOGE("%s", reinterpret_cast<const char*>(translated_binary_.data()));
|
||||
translated_binary_.pop_back();
|
||||
}
|
||||
error_blob->Release();
|
||||
}
|
||||
|
||||
if (!compiled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (FLAGS_d3d12_shader_disasm) {
|
||||
ID3DBlob* disassembly_blob;
|
||||
if (SUCCEEDED(D3DDisassemble(blob_->GetBufferPointer(),
|
||||
blob_->GetBufferSize(), 0, nullptr,
|
||||
&disassembly_blob))) {
|
||||
host_disassembly_ =
|
||||
reinterpret_cast<const char*>(disassembly_blob->GetBufferPointer());
|
||||
disassembly_blob->Release();
|
||||
} else {
|
||||
XELOGE("Failed to disassemble DXBC for %s shader %.16llX", target,
|
||||
ucode_data_hash());
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
const uint8_t* D3D12Shader::GetDXBC() const {
|
||||
assert_not_null(blob_);
|
||||
return reinterpret_cast<const uint8_t*>(blob_->GetBufferPointer());
|
||||
}
|
||||
|
||||
size_t D3D12Shader::GetDXBCSize() const {
|
||||
assert_not_null(blob_);
|
||||
return blob_->GetBufferSize();
|
||||
}
|
||||
|
||||
} // namespace d3d12
|
||||
} // namespace gpu
|
||||
} // namespace xe
|
||||
39
src/xenia/gpu/d3d12/d3d12_shader.h
Normal file
39
src/xenia/gpu/d3d12/d3d12_shader.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2018 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_GPU_D3D12_D3D12_SHADER_H_
|
||||
#define XENIA_GPU_D3D12_D3D12_SHADER_H_
|
||||
|
||||
#include "xenia/gpu/shader.h"
|
||||
#include "xenia/ui/d3d12/d3d12_api.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
namespace d3d12 {
|
||||
|
||||
class D3D12Shader : public Shader {
|
||||
public:
|
||||
D3D12Shader(ShaderType shader_type, uint64_t data_hash,
|
||||
const uint32_t* dword_ptr, uint32_t dword_count);
|
||||
~D3D12Shader() override;
|
||||
|
||||
bool Prepare();
|
||||
|
||||
const uint8_t* GetDXBC() const;
|
||||
size_t GetDXBCSize() const;
|
||||
|
||||
private:
|
||||
ID3DBlob* blob_ = nullptr;
|
||||
};
|
||||
|
||||
} // namespace d3d12
|
||||
} // namespace gpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_GPU_D3D12_D3D12_SHADER_H_
|
||||
199
src/xenia/gpu/d3d12/pipeline_cache.cc
Normal file
199
src/xenia/gpu/d3d12/pipeline_cache.cc
Normal file
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2018 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#include "xenia/gpu/d3d12/pipeline_cache.h"
|
||||
|
||||
#include <cinttypes>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/base/logging.h"
|
||||
#include "xenia/gpu/gpu_flags.h"
|
||||
#include "xenia/gpu/hlsl_shader_translator.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
namespace d3d12 {
|
||||
|
||||
PipelineCache::PipelineCache(RegisterFile* register_file,
|
||||
ui::d3d12::D3D12Context* context)
|
||||
: register_file_(register_file), context_(context) {
|
||||
shader_translator_.reset(new HlslShaderTranslator());
|
||||
}
|
||||
|
||||
PipelineCache::~PipelineCache() { Shutdown(); }
|
||||
|
||||
void PipelineCache::Shutdown() {
|
||||
ClearCache();
|
||||
}
|
||||
|
||||
D3D12Shader* PipelineCache::LoadShader(ShaderType shader_type,
|
||||
uint32_t guest_address,
|
||||
const uint32_t* host_address,
|
||||
uint32_t dword_count) {
|
||||
// Hash the input memory and lookup the shader.
|
||||
uint64_t data_hash = XXH64(host_address, dword_count * sizeof(uint32_t), 0);
|
||||
auto it = shader_map_.find(data_hash);
|
||||
if (it != shader_map_.end()) {
|
||||
// Shader has been previously loaded.
|
||||
return it->second;
|
||||
}
|
||||
|
||||
// Always create the shader and stash it away.
|
||||
// We need to track it even if it fails translation so we know not to try
|
||||
// again.
|
||||
D3D12Shader* shader = new D3D12Shader(shader_type, data_hash, host_address,
|
||||
dword_count);
|
||||
shader_map_.insert({data_hash, shader});
|
||||
|
||||
return shader;
|
||||
}
|
||||
|
||||
PipelineCache::UpdateStatus PipelineCache::ConfigurePipeline(
|
||||
D3D12Shader* vertex_shader, D3D12Shader* pixel_shader,
|
||||
PrimitiveType primitive_type) {
|
||||
#if FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // FINE_GRAINED_DRAW_SCOPES
|
||||
return UpdateState(vertex_shader, pixel_shader, primitive_type);
|
||||
}
|
||||
|
||||
void PipelineCache::ClearCache() {
|
||||
// Destroy all shaders.
|
||||
for (auto it : shader_map_) {
|
||||
delete it.second;
|
||||
}
|
||||
shader_map_.clear();
|
||||
}
|
||||
|
||||
bool PipelineCache::SetShadowRegister(uint32_t* dest, uint32_t register_name) {
|
||||
uint32_t value = register_file_->values[register_name].u32;
|
||||
if (*dest == value) {
|
||||
return false;
|
||||
}
|
||||
*dest = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PipelineCache::SetShadowRegister(float* dest, uint32_t register_name) {
|
||||
float value = register_file_->values[register_name].f32;
|
||||
if (*dest == value) {
|
||||
return false;
|
||||
}
|
||||
*dest = value;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool PipelineCache::TranslateShader(D3D12Shader* shader,
|
||||
xenos::xe_gpu_program_cntl_t cntl) {
|
||||
// Perform translation.
|
||||
// If this fails the shader will be marked as invalid and ignored later.
|
||||
if (!shader_translator_->Translate(shader, cntl)) {
|
||||
XELOGE("Shader translation failed; marking shader as ignored");
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prepare the shader for use (creates the Shader Model bytecode).
|
||||
// It could still fail at this point.
|
||||
if (!shader->Prepare()) {
|
||||
XELOGE("Shader preparation failed; marking shader as ignored");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (shader->is_valid()) {
|
||||
XELOGGPU("Generated %s shader (%db) - hash %.16" PRIX64 ":\n%s\n",
|
||||
shader->type() == ShaderType::kVertex ? "vertex" : "pixel",
|
||||
shader->ucode_dword_count() * 4, shader->ucode_data_hash(),
|
||||
shader->ucode_disassembly().c_str());
|
||||
}
|
||||
|
||||
// Dump shader files if desired.
|
||||
if (!FLAGS_dump_shaders.empty()) {
|
||||
shader->Dump(FLAGS_dump_shaders, "d3d12");
|
||||
}
|
||||
|
||||
return shader->is_valid();
|
||||
}
|
||||
|
||||
PipelineCache::UpdateStatus PipelineCache::UpdateState(
|
||||
D3D12Shader* vertex_shader, D3D12Shader* pixel_shader,
|
||||
PrimitiveType primitive_type) {
|
||||
bool mismatch = false;
|
||||
|
||||
// Reset hash so we can build it up.
|
||||
XXH64_reset(&hash_state_, 0);
|
||||
|
||||
#define CHECK_UPDATE_STATUS(status, mismatch, error_message) \
|
||||
{ \
|
||||
if (status == UpdateStatus::kError) { \
|
||||
XELOGE(error_message); \
|
||||
return status; \
|
||||
} else if (status == UpdateStatus::kMismatch) { \
|
||||
mismatch = true; \
|
||||
} \
|
||||
}
|
||||
|
||||
UpdateStatus status;
|
||||
status = UpdateShaderStages(vertex_shader, pixel_shader, primitive_type);
|
||||
CHECK_UPDATE_STATUS(status, mismatch, "Unable to update shader stages");
|
||||
|
||||
#undef CHECK_UPDATE_STATUS
|
||||
|
||||
return mismatch ? UpdateStatus::kMismatch : UpdateStatus::kCompatible;
|
||||
}
|
||||
|
||||
PipelineCache::UpdateStatus PipelineCache::UpdateShaderStages(
|
||||
D3D12Shader* vertex_shader, D3D12Shader* pixel_shader,
|
||||
PrimitiveType primitive_type) {
|
||||
auto& regs = update_shader_stages_regs_;
|
||||
|
||||
// These are the constant base addresses/ranges for shaders.
|
||||
// We have these hardcoded right now cause nothing seems to differ.
|
||||
assert_true(register_file_->values[XE_GPU_REG_SQ_VS_CONST].u32 ==
|
||||
0x000FF000 ||
|
||||
register_file_->values[XE_GPU_REG_SQ_VS_CONST].u32 == 0x00000000);
|
||||
assert_true(register_file_->values[XE_GPU_REG_SQ_PS_CONST].u32 ==
|
||||
0x000FF100 ||
|
||||
register_file_->values[XE_GPU_REG_SQ_PS_CONST].u32 == 0x00000000);
|
||||
|
||||
bool dirty = false;
|
||||
dirty |= SetShadowRegister(®s.pa_su_sc_mode_cntl,
|
||||
XE_GPU_REG_PA_SU_SC_MODE_CNTL);
|
||||
dirty |= SetShadowRegister(®s.sq_program_cntl, XE_GPU_REG_SQ_PROGRAM_CNTL);
|
||||
dirty |= regs.vertex_shader != vertex_shader;
|
||||
dirty |= regs.pixel_shader != pixel_shader;
|
||||
dirty |= regs.primitive_type != primitive_type;
|
||||
regs.vertex_shader = vertex_shader;
|
||||
regs.pixel_shader = pixel_shader;
|
||||
regs.primitive_type = primitive_type;
|
||||
XXH64_update(&hash_state_, ®s, sizeof(regs));
|
||||
if (!dirty) {
|
||||
return UpdateStatus::kCompatible;
|
||||
}
|
||||
|
||||
xenos::xe_gpu_program_cntl_t sq_program_cntl;
|
||||
sq_program_cntl.dword_0 = regs.sq_program_cntl;
|
||||
|
||||
if (!vertex_shader->is_translated() &&
|
||||
!TranslateShader(vertex_shader, sq_program_cntl)) {
|
||||
XELOGE("Failed to translate the vertex shader!");
|
||||
return UpdateStatus::kError;
|
||||
}
|
||||
|
||||
if (pixel_shader && !pixel_shader->is_translated() &&
|
||||
!TranslateShader(pixel_shader, sq_program_cntl)) {
|
||||
XELOGE("Failed to translate the pixel shader!");
|
||||
return UpdateStatus::kError;
|
||||
}
|
||||
|
||||
return UpdateStatus::kMismatch;
|
||||
}
|
||||
|
||||
} // namespace d3d12
|
||||
} // namespace gpu
|
||||
} // namespace xe
|
||||
93
src/xenia/gpu/d3d12/pipeline_cache.h
Normal file
93
src/xenia/gpu/d3d12/pipeline_cache.h
Normal file
@@ -0,0 +1,93 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2018 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_GPU_D3D12_PIPELINE_CACHE_H_
|
||||
#define XENIA_GPU_D3D12_PIPELINE_CACHE_H_
|
||||
|
||||
#include <unordered_map>
|
||||
|
||||
#include "third_party/xxhash/xxhash.h"
|
||||
|
||||
#include "xenia/gpu/d3d12/d3d12_shader.h"
|
||||
#include "xenia/gpu/register_file.h"
|
||||
#include "xenia/gpu/shader_translator.h"
|
||||
#include "xenia/gpu/xenos.h"
|
||||
#include "xenia/ui/d3d12/d3d12_context.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
namespace d3d12 {
|
||||
|
||||
class PipelineCache {
|
||||
public:
|
||||
enum class UpdateStatus {
|
||||
kCompatible,
|
||||
kMismatch,
|
||||
kError,
|
||||
};
|
||||
|
||||
PipelineCache(RegisterFile* register_file, ui::d3d12::D3D12Context* context);
|
||||
~PipelineCache();
|
||||
|
||||
void Shutdown();
|
||||
|
||||
D3D12Shader* LoadShader(ShaderType shader_type, uint32_t guest_address,
|
||||
const uint32_t* host_address, uint32_t dword_count);
|
||||
|
||||
UpdateStatus ConfigurePipeline(D3D12Shader* vertex_shader,
|
||||
D3D12Shader* pixel_shader,
|
||||
PrimitiveType primitive_type);
|
||||
|
||||
void ClearCache();
|
||||
|
||||
private:
|
||||
bool SetShadowRegister(uint32_t* dest, uint32_t register_name);
|
||||
bool SetShadowRegister(float* dest, uint32_t register_name);
|
||||
|
||||
bool TranslateShader(D3D12Shader* shader, xenos::xe_gpu_program_cntl_t cntl);
|
||||
|
||||
UpdateStatus UpdateState(D3D12Shader* vertex_shader,
|
||||
D3D12Shader* pixel_shader,
|
||||
PrimitiveType primitive_type);
|
||||
|
||||
UpdateStatus UpdateShaderStages(D3D12Shader* vertex_shader,
|
||||
D3D12Shader* pixel_shader,
|
||||
PrimitiveType primitive_type);
|
||||
|
||||
RegisterFile* register_file_ = nullptr;
|
||||
ui::d3d12::D3D12Context* context_ = nullptr;
|
||||
|
||||
// Reusable shader translator.
|
||||
std::unique_ptr<ShaderTranslator> shader_translator_ = nullptr;
|
||||
// All loaded shaders mapped by their guest hash key.
|
||||
std::unordered_map<uint64_t, D3D12Shader*> shader_map_;
|
||||
|
||||
// Hash state used to incrementally produce pipeline hashes during update.
|
||||
// By the time the full update pass has run the hash will represent the
|
||||
// current state in a way that can uniquely identify the produced
|
||||
// ID3D12PipelineState.
|
||||
XXH64_state_t hash_state_;
|
||||
|
||||
struct UpdateShaderStagesRegisters {
|
||||
PrimitiveType primitive_type;
|
||||
uint32_t pa_su_sc_mode_cntl;
|
||||
uint32_t sq_program_cntl;
|
||||
D3D12Shader* vertex_shader;
|
||||
D3D12Shader* pixel_shader;
|
||||
|
||||
UpdateShaderStagesRegisters() { Reset(); }
|
||||
void Reset() { std::memset(this, 0, sizeof(*this)); }
|
||||
} update_shader_stages_regs_;
|
||||
};
|
||||
|
||||
} // namespace d3d12
|
||||
} // namespace gpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_GPU_D3D12_PIPELINE_CACHE_H_
|
||||
Reference in New Issue
Block a user