mirror of
https://github.com/xenios-jp/XeniOS.git
synced 2026-07-11 15:19:09 -07:00
[GPU/Metal] Add Metal Shader Converter backend
This commit is contained in:
@@ -154,11 +154,11 @@ jobs:
|
||||
|
||||
- name: Setup
|
||||
run: |
|
||||
# Update submodules. Skip DirectXShaderCompiler (we don't use the
|
||||
# Metal shader converter / dxilconv pipeline on edge).
|
||||
# Update submodules.
|
||||
git submodule update --init --depth=1 -j$(sysctl -n hw.ncpu) \
|
||||
$(grep -oE 'path = .+' .gitmodules | sed 's/path = //' \
|
||||
| grep -v 'DirectXShaderCompiler')
|
||||
$(grep -oE 'path = .+' .gitmodules | sed 's/path = //')
|
||||
git -C third_party/DirectXShaderCompiler submodule update --init --depth=1 \
|
||||
external/SPIRV-Headers external/SPIRV-Tools
|
||||
|
||||
# wxWidgets has nested submodules (pcre, libpng, ...) needed when
|
||||
# building from vendored source.
|
||||
@@ -245,8 +245,9 @@ jobs:
|
||||
- name: Setup
|
||||
run: |
|
||||
git submodule update --init --depth=1 -j$(sysctl -n hw.ncpu) \
|
||||
$(grep -oE 'path = .+' .gitmodules | sed 's/path = //' \
|
||||
| grep -v 'DirectXShaderCompiler')
|
||||
$(grep -oE 'path = .+' .gitmodules | sed 's/path = //')
|
||||
git -C third_party/DirectXShaderCompiler submodule update --init --depth=1 \
|
||||
external/SPIRV-Headers external/SPIRV-Tools
|
||||
git submodule update --init --recursive --depth=1 \
|
||||
-j$(sysctl -n hw.ncpu) third_party/wxWidgets
|
||||
./xenia-build.py fetchdata
|
||||
|
||||
+6
-1
@@ -40,7 +40,8 @@
|
||||
url = https://github.com/xenia-canary/disruptorplus.git
|
||||
[submodule "third_party/DirectXShaderCompiler"]
|
||||
path = third_party/DirectXShaderCompiler
|
||||
url = https://github.com/microsoft/DirectXShaderCompiler.git
|
||||
url = https://github.com/xenios-jp/DirectXShaderCompiler.git
|
||||
branch = dxilconv-ios-support
|
||||
[submodule "third_party/date"]
|
||||
path = third_party/date
|
||||
url = https://github.com/HowardHinnant/date.git
|
||||
@@ -130,3 +131,7 @@
|
||||
[submodule "third_party/boost_context/context"]
|
||||
path = third_party/boost_context/context
|
||||
url = https://github.com/boostorg/context.git
|
||||
[submodule "third_party/metal-shader-converter"]
|
||||
path = third_party/metal-shader-converter
|
||||
url = https://github.com/xenios-jp/metal-shader-converter
|
||||
branch = ios-support-3.1.1
|
||||
|
||||
@@ -300,6 +300,7 @@ else()
|
||||
CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 20)
|
||||
add_compile_options(
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-Wno-deprecated-literal-operator>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-Wno-invalid-specialization>
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-Wno-nontrivial-memcall>
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -183,6 +183,20 @@ elseif(APPLE)
|
||||
target_link_options(xenia-app PRIVATE
|
||||
"-Wl,-rpath,@executable_path/../Frameworks"
|
||||
"-Wl,-rpath,@loader_path/../Frameworks")
|
||||
|
||||
foreach(_xe_app_runtime_dylib
|
||||
xenia-third-party-metal-shader-converter
|
||||
xenia-third-party-dxilconv)
|
||||
if(TARGET ${_xe_app_runtime_dylib})
|
||||
add_custom_command(TARGET xenia-app POST_BUILD
|
||||
COMMAND "${CMAKE_COMMAND}" -E make_directory
|
||||
"$<TARGET_BUNDLE_DIR:xenia-app>/Contents/Frameworks"
|
||||
COMMAND "${CMAKE_COMMAND}" -E copy_if_different
|
||||
"$<TARGET_FILE:${_xe_app_runtime_dylib}>"
|
||||
"$<TARGET_BUNDLE_DIR:xenia-app>/Contents/Frameworks/$<TARGET_FILE_NAME:${_xe_app_runtime_dylib}>"
|
||||
VERBATIM)
|
||||
endif()
|
||||
endforeach()
|
||||
endif()
|
||||
|
||||
xe_target_defaults(xenia-app)
|
||||
|
||||
@@ -61,7 +61,8 @@ namespace a64 {
|
||||
uint64_t ResolveFunction(void* raw_context, uint64_t target_address);
|
||||
|
||||
uint32_t FindStackpointSyncDepth(const A64BackendStackpoint* stackpoints,
|
||||
uint32_t current_depth, uint32_t guest_sp) {
|
||||
uint32_t current_depth, uint32_t guest_sp,
|
||||
uint32_t guest_return_address) {
|
||||
if (!stackpoints || current_depth == 0) {
|
||||
return 0;
|
||||
}
|
||||
@@ -77,6 +78,26 @@ uint32_t FindStackpointSyncDepth(const A64BackendStackpoint* stackpoints,
|
||||
if (idx == 0xFFFFFFFFu || frames_skipped <= 1) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// x64 breaks ties between equal guest stacks with guest_return_address_ and
|
||||
// restores the caller of the matching frame. Without this, A64 can choose a
|
||||
// deeper equal-stack frame and resume a return-site with the wrong host SP.
|
||||
if (guest_return_address) {
|
||||
const uint32_t matching_guest_sp = stackpoints[idx].guest_stack_;
|
||||
uint32_t search_idx = idx;
|
||||
while (stackpoints[search_idx].guest_stack_ == matching_guest_sp) {
|
||||
if (stackpoints[search_idx].guest_return_address_ ==
|
||||
guest_return_address) {
|
||||
return search_idx == 0 ? 0 : search_idx;
|
||||
}
|
||||
if (search_idx == 0) {
|
||||
return 1;
|
||||
}
|
||||
--search_idx;
|
||||
}
|
||||
return search_idx + 2;
|
||||
}
|
||||
|
||||
return idx + 1;
|
||||
}
|
||||
|
||||
@@ -431,8 +452,9 @@ void* A64HelperEmitter::EmitGuestAndHostSynchronizeStackHelper() {
|
||||
current_stackpoint_depth))));
|
||||
|
||||
// w13 = target depth computed by ResolveFunction.
|
||||
ldr(w13, ptr(x19, static_cast<uint32_t>(offsetof(
|
||||
A64BackendContext, pending_stackpoint_sync_depth))));
|
||||
ldr(w13, ptr(x19, static_cast<uint32_t>(
|
||||
offsetof(A64BackendContext,
|
||||
pending_stackpoint_sync_depth))));
|
||||
auto& underflow = NewCachedLabel();
|
||||
|
||||
cbz(x10, underflow);
|
||||
@@ -462,8 +484,9 @@ void* A64HelperEmitter::EmitGuestAndHostSynchronizeStackHelper() {
|
||||
str(w13, ptr(x19, static_cast<uint32_t>(offsetof(A64BackendContext,
|
||||
current_stackpoint_depth))));
|
||||
mov(w15, 0);
|
||||
str(w15, ptr(x19, static_cast<uint32_t>(offsetof(
|
||||
A64BackendContext, pending_stackpoint_sync_depth))));
|
||||
str(w15, ptr(x19, static_cast<uint32_t>(
|
||||
offsetof(A64BackendContext,
|
||||
pending_stackpoint_sync_depth))));
|
||||
|
||||
// Jump back to the caller.
|
||||
br(x8);
|
||||
@@ -647,7 +670,8 @@ uint64_t ResolveFunction(void* raw_context, uint64_t target_address) {
|
||||
const uint32_t sync_depth = FindStackpointSyncDepth(
|
||||
backend_context->stackpoints,
|
||||
backend_context->current_stackpoint_depth,
|
||||
static_cast<uint32_t>(guest_context->r[1]));
|
||||
static_cast<uint32_t>(guest_context->r[1]),
|
||||
static_cast<uint32_t>(target_address));
|
||||
if (sync_depth != 0) {
|
||||
backend_context->pending_stackpoint_sync_depth = sync_depth;
|
||||
return host_address;
|
||||
|
||||
@@ -55,7 +55,8 @@ struct A64BackendStackpoint {
|
||||
};
|
||||
|
||||
uint32_t FindStackpointSyncDepth(const A64BackendStackpoint* stackpoints,
|
||||
uint32_t current_depth, uint32_t guest_sp);
|
||||
uint32_t current_depth, uint32_t guest_sp,
|
||||
uint32_t guest_return_address);
|
||||
|
||||
enum : uint32_t {
|
||||
kA64BackendFPCRModeBit = 0,
|
||||
|
||||
@@ -738,8 +738,9 @@ void A64Emitter::EnsureSynchronizedGuestAndHostStack() {
|
||||
// still point at a skipped frame here.
|
||||
auto& return_from_sync = NewCachedLabel();
|
||||
|
||||
ldr(w16, ptr(x19, static_cast<uint32_t>(offsetof(
|
||||
A64BackendContext, pending_stackpoint_sync_depth))));
|
||||
ldr(w16, ptr(x19, static_cast<uint32_t>(
|
||||
offsetof(A64BackendContext,
|
||||
pending_stackpoint_sync_depth))));
|
||||
cbz(w16, return_from_sync);
|
||||
|
||||
auto& sync_label = AddToTail([](A64Emitter& e, Label& lbl) {
|
||||
|
||||
@@ -192,6 +192,178 @@ TEST_CASE("HOST_GUEST_HOST_ROUNDTRIP", "[backend]") {
|
||||
memory->SystemHeapFree(stack_address);
|
||||
}
|
||||
|
||||
#if XE_ARCH_ARM64
|
||||
TEST_CASE("A64_STACKPOINT_SYNC_DEPTH_SELECTION", "[backend]") {
|
||||
using xe::cpu::backend::a64::A64BackendStackpoint;
|
||||
using xe::cpu::backend::a64::FindStackpointSyncDepth;
|
||||
|
||||
A64BackendStackpoint stackpoints[4] = {};
|
||||
stackpoints[0].guest_stack_ = 0xA000;
|
||||
stackpoints[1].guest_stack_ = 0x9000;
|
||||
stackpoints[2].guest_stack_ = 0x8000;
|
||||
stackpoints[3].guest_stack_ = 0x7000;
|
||||
|
||||
REQUIRE(FindStackpointSyncDepth(nullptr, 4, 0x8500, 0x8123456C) == 0);
|
||||
REQUIRE(FindStackpointSyncDepth(stackpoints, 0, 0x8500, 0x8123456C) == 0);
|
||||
|
||||
// Two skipped deeper frames: target stackpoint depth is the existing caller.
|
||||
REQUIRE(FindStackpointSyncDepth(stackpoints, 4, 0x8500, 0x8123456C) == 2);
|
||||
|
||||
// One skipped frame can be an early guest SP restore, so it must not repair.
|
||||
REQUIRE(FindStackpointSyncDepth(stackpoints, 4, 0x7500, 0x8123456C) == 0);
|
||||
|
||||
// Guest SP unwound past every recorded stackpoint: no safe repair target.
|
||||
REQUIRE(FindStackpointSyncDepth(stackpoints, 4, 0xB000, 0x8123456C) == 0);
|
||||
}
|
||||
|
||||
TEST_CASE("A64_STACKPOINT_SYNC_DEPTH_DISAMBIGUATES_EQUAL_GUEST_STACKS",
|
||||
"[backend]") {
|
||||
using xe::cpu::backend::a64::A64BackendStackpoint;
|
||||
using xe::cpu::backend::a64::FindStackpointSyncDepth;
|
||||
|
||||
A64BackendStackpoint stackpoints[5] = {};
|
||||
stackpoints[0].guest_stack_ = 0xA000;
|
||||
stackpoints[0].guest_return_address_ = 0x80000100;
|
||||
stackpoints[1].guest_stack_ = 0x9000;
|
||||
stackpoints[1].guest_return_address_ = 0x80000200;
|
||||
stackpoints[2].guest_stack_ = 0x9000;
|
||||
stackpoints[2].guest_return_address_ = 0x80000300;
|
||||
stackpoints[3].guest_stack_ = 0x8000;
|
||||
stackpoints[3].guest_return_address_ = 0x80000400;
|
||||
stackpoints[4].guest_stack_ = 0x7000;
|
||||
stackpoints[4].guest_return_address_ = 0x80000500;
|
||||
|
||||
// The guest SP search stops at index 2. The return address identifies the
|
||||
// frame being popped, so the sync target is its caller at depth 2.
|
||||
REQUIRE(FindStackpointSyncDepth(stackpoints, 5, 0x8800, 0x80000300) == 2);
|
||||
|
||||
// A match at the shallower equal-stack frame pops to its caller.
|
||||
REQUIRE(FindStackpointSyncDepth(stackpoints, 5, 0x8800, 0x80000200) == 1);
|
||||
|
||||
// If no return address in the equal-stack group matches, mirror x64 by
|
||||
// choosing the shallowest equal-stack frame rather than the deepest one.
|
||||
REQUIRE(FindStackpointSyncDepth(stackpoints, 5, 0x8800, 0x80000900) == 2);
|
||||
}
|
||||
|
||||
static std::atomic<uint32_t> a64_pending_sync_builtin_count{0};
|
||||
static uint32_t a64_pending_sync_observed_depth = 0;
|
||||
|
||||
static void MarkA64PendingSyncForCaller(ppc::PPCContext* ctx, void* arg0,
|
||||
void* arg1) {
|
||||
auto* a64_backend = static_cast<xe::cpu::backend::a64::A64Backend*>(
|
||||
ctx->thread_state->processor()->backend());
|
||||
auto* bctx = a64_backend->BackendContextForGuestContext(ctx);
|
||||
a64_pending_sync_observed_depth = bctx->current_stackpoint_depth;
|
||||
if (bctx->current_stackpoint_depth > 1) {
|
||||
bctx->pending_stackpoint_sync_depth = bctx->current_stackpoint_depth - 1;
|
||||
}
|
||||
a64_pending_sync_builtin_count.fetch_add(1);
|
||||
}
|
||||
|
||||
TEST_CASE("A64_PENDING_STACKPOINT_SYNC_AFTER_GUEST_CALL", "[backend]") {
|
||||
constexpr uint32_t kCallerAddr = 0x80000000;
|
||||
constexpr uint32_t kCalleeAddr = 0x80001000;
|
||||
constexpr uint64_t kPostCallMarker = 0xA64A64A64A64A64Aull;
|
||||
|
||||
a64_pending_sync_builtin_count = 0;
|
||||
a64_pending_sync_observed_depth = 0;
|
||||
|
||||
auto memory = std::make_unique<Memory>();
|
||||
memory->Initialize();
|
||||
|
||||
auto backend = CreateBackend();
|
||||
REQUIRE(backend);
|
||||
|
||||
auto processor = std::make_unique<Processor>(memory.get(), nullptr);
|
||||
processor->Setup(std::move(backend));
|
||||
|
||||
auto* marker_fn = processor->DefineBuiltin(
|
||||
"MarkA64PendingSync", MarkA64PendingSyncForCaller, nullptr, nullptr);
|
||||
|
||||
int gen_invocation = 0;
|
||||
Function* callee_fn = nullptr;
|
||||
auto module = std::make_unique<TestModule>(
|
||||
processor.get(), "Test",
|
||||
[](uint32_t address) {
|
||||
return address == kCallerAddr || address == kCalleeAddr;
|
||||
},
|
||||
[&](HIRBuilder& b) {
|
||||
if (gen_invocation++ == 0) {
|
||||
b.CallExtern(marker_fn);
|
||||
b.Return();
|
||||
} else {
|
||||
REQUIRE(callee_fn != nullptr);
|
||||
b.Call(callee_fn);
|
||||
StoreGPR(b, 3, b.LoadConstantUint64(kPostCallMarker));
|
||||
b.Return();
|
||||
}
|
||||
return true;
|
||||
},
|
||||
/*skip_cf_simplification=*/true);
|
||||
processor->AddModule(std::move(module));
|
||||
processor->backend()->CommitExecutableRange(kCallerAddr,
|
||||
kCalleeAddr + 0x1000);
|
||||
|
||||
callee_fn = processor->ResolveFunction(kCalleeAddr);
|
||||
REQUIRE(callee_fn != nullptr);
|
||||
auto* caller_fn = processor->ResolveFunction(kCallerAddr);
|
||||
REQUIRE(caller_fn != nullptr);
|
||||
|
||||
uint32_t stack_size = 64 * 1024;
|
||||
uint32_t stack_address = memory->SystemHeapAlloc(stack_size);
|
||||
auto thread_state = std::make_unique<ThreadState>(processor.get(), 0x100,
|
||||
stack_address + stack_size);
|
||||
auto ctx = thread_state->context();
|
||||
ctx->lr = 0xBCBCBCBC;
|
||||
ctx->r[3] = 0;
|
||||
|
||||
caller_fn->Call(thread_state.get(), uint32_t(ctx->lr));
|
||||
|
||||
auto* a64_backend =
|
||||
static_cast<xe::cpu::backend::a64::A64Backend*>(processor->backend());
|
||||
auto* bctx = a64_backend->BackendContextForGuestContext(ctx);
|
||||
|
||||
REQUIRE(a64_pending_sync_builtin_count == 1);
|
||||
REQUIRE(a64_pending_sync_observed_depth > 1);
|
||||
REQUIRE(ctx->r[3] == kPostCallMarker);
|
||||
REQUIRE(bctx->pending_stackpoint_sync_depth == 0);
|
||||
REQUIRE(bctx->current_stackpoint_depth == 0);
|
||||
|
||||
memory->SystemHeapFree(stack_address);
|
||||
}
|
||||
|
||||
TEST_CASE("A64_PREPARE_FOR_REENTRY_CLEARS_PENDING_STACKPOINT_SYNC",
|
||||
"[backend]") {
|
||||
auto memory = std::make_unique<Memory>();
|
||||
memory->Initialize();
|
||||
|
||||
auto backend = CreateBackend();
|
||||
REQUIRE(backend);
|
||||
|
||||
auto processor = std::make_unique<Processor>(memory.get(), nullptr);
|
||||
processor->Setup(std::move(backend));
|
||||
|
||||
uint32_t stack_size = 64 * 1024;
|
||||
uint32_t stack_address = memory->SystemHeapAlloc(stack_size);
|
||||
auto thread_state = std::make_unique<ThreadState>(processor.get(), 0x100,
|
||||
stack_address + stack_size);
|
||||
auto ctx = thread_state->context();
|
||||
|
||||
auto* a64_backend =
|
||||
static_cast<xe::cpu::backend::a64::A64Backend*>(processor->backend());
|
||||
auto* bctx = a64_backend->BackendContextForGuestContext(ctx);
|
||||
bctx->current_stackpoint_depth = 3;
|
||||
bctx->pending_stackpoint_sync_depth = 2;
|
||||
|
||||
processor->backend()->PrepareForReentry(ctx);
|
||||
|
||||
REQUIRE(bctx->current_stackpoint_depth == 0);
|
||||
REQUIRE(bctx->pending_stackpoint_sync_depth == 0);
|
||||
|
||||
memory->SystemHeapFree(stack_address);
|
||||
}
|
||||
#endif // XE_ARCH_ARM64
|
||||
|
||||
// =============================================================================
|
||||
// GPR preservation across GuestToHostThunk
|
||||
// =============================================================================
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -32,6 +32,7 @@
|
||||
#include "xenia/base/threading.h"
|
||||
#include "xenia/gpu/d3d12/d3d12_render_target_cache.h"
|
||||
#include "xenia/gpu/d3d12/d3d12_shader.h"
|
||||
#include "xenia/gpu/dxbc_geometry_shader.h"
|
||||
#include "xenia/gpu/dxbc_shader_translator.h"
|
||||
#include "xenia/gpu/gpu_flags.h"
|
||||
#include "xenia/gpu/primitive_processor.h"
|
||||
@@ -151,12 +152,9 @@ class PipelineCache {
|
||||
kTriangle,
|
||||
};
|
||||
|
||||
enum class PipelineGeometryShader : uint32_t {
|
||||
kNone,
|
||||
kPointList,
|
||||
kRectangleList,
|
||||
kQuadList,
|
||||
};
|
||||
// Geometry-shader key + DXBC generation are shared across backends; see
|
||||
// xenia/gpu/dxbc_geometry_shader.h.
|
||||
using PipelineGeometryShader = xe::gpu::PipelineGeometryShader;
|
||||
|
||||
enum class PipelineCullMode : uint32_t {
|
||||
kNone,
|
||||
@@ -251,32 +249,7 @@ class PipelineCache {
|
||||
PipelineDescription description;
|
||||
};
|
||||
|
||||
union GeometryShaderKey {
|
||||
uint32_t key;
|
||||
struct {
|
||||
PipelineGeometryShader type : 2;
|
||||
uint32_t interpolator_count : 5;
|
||||
uint32_t user_clip_plane_count : 3;
|
||||
uint32_t user_clip_plane_cull : 1;
|
||||
uint32_t has_vertex_kill_and : 1;
|
||||
uint32_t has_point_size : 1;
|
||||
uint32_t has_point_coordinates : 1;
|
||||
};
|
||||
|
||||
GeometryShaderKey() : key(0) { static_assert_size(*this, sizeof(key)); }
|
||||
|
||||
struct Hasher {
|
||||
size_t operator()(const GeometryShaderKey& key) const {
|
||||
return std::hash<uint32_t>{}(key.key);
|
||||
}
|
||||
};
|
||||
bool operator==(const GeometryShaderKey& other_key) const {
|
||||
return key == other_key.key;
|
||||
}
|
||||
bool operator!=(const GeometryShaderKey& other_key) const {
|
||||
return !(*this == other_key);
|
||||
}
|
||||
};
|
||||
using GeometryShaderKey = xe::gpu::GeometryShaderKey;
|
||||
|
||||
D3D12Shader* LoadShader(xenos::ShaderType shader_type,
|
||||
const uint32_t* host_address, uint32_t dword_count,
|
||||
@@ -312,13 +285,6 @@ class PipelineCache {
|
||||
PipelineRuntimeDescription& runtime_description_out,
|
||||
bool for_placeholder = false);
|
||||
|
||||
static bool GetGeometryShaderKey(
|
||||
PipelineGeometryShader geometry_shader_type,
|
||||
DxbcShaderTranslator::Modification vertex_shader_modification,
|
||||
DxbcShaderTranslator::Modification pixel_shader_modification,
|
||||
GeometryShaderKey& key_out);
|
||||
static void CreateDxbcGeometryShader(GeometryShaderKey key,
|
||||
std::vector<uint32_t>& shader_out);
|
||||
const std::vector<uint32_t>& GetGeometryShader(GeometryShaderKey key);
|
||||
|
||||
ID3D12PipelineState* CreateD3D12Pipeline(
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,77 @@
|
||||
/**
|
||||
******************************************************************************
|
||||
* Xenia : Xbox 360 Emulator Research Project *
|
||||
******************************************************************************
|
||||
* Copyright 2026 Ben Vanik. All rights reserved. *
|
||||
* Released under the BSD license - see LICENSE in the root for more details. *
|
||||
******************************************************************************
|
||||
*/
|
||||
|
||||
#ifndef XENIA_GPU_DXBC_GEOMETRY_SHADER_H_
|
||||
#define XENIA_GPU_DXBC_GEOMETRY_SHADER_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <vector>
|
||||
|
||||
#include "xenia/base/assert.h"
|
||||
#include "xenia/gpu/dxbc_shader_translator.h"
|
||||
|
||||
namespace xe {
|
||||
namespace gpu {
|
||||
|
||||
// Geometry-shader emulation modes shared by the host backends. The Xbox 360
|
||||
// expands point/rectangle/quad lists that desktop APIs render via a geometry
|
||||
// shader; this DXBC generator is shared by the D3D12 and Metal backends (Metal
|
||||
// then converts the DXBC through the Metal Shader Converter). Vulkan uses a
|
||||
// separate SPIR-V path and is intentionally not part of this module.
|
||||
enum class PipelineGeometryShader : uint32_t {
|
||||
kNone,
|
||||
kPointList,
|
||||
kRectangleList,
|
||||
kQuadList,
|
||||
};
|
||||
|
||||
union GeometryShaderKey {
|
||||
uint32_t key;
|
||||
struct {
|
||||
PipelineGeometryShader type : 2;
|
||||
uint32_t interpolator_count : 5;
|
||||
uint32_t user_clip_plane_count : 3;
|
||||
uint32_t user_clip_plane_cull : 1;
|
||||
uint32_t has_vertex_kill_and : 1;
|
||||
uint32_t has_point_size : 1;
|
||||
uint32_t has_point_coordinates : 1;
|
||||
};
|
||||
|
||||
GeometryShaderKey() : key(0) { static_assert_size(*this, sizeof(key)); }
|
||||
|
||||
struct Hasher {
|
||||
size_t operator()(const GeometryShaderKey& key) const {
|
||||
return std::hash<uint32_t>{}(key.key);
|
||||
}
|
||||
};
|
||||
bool operator==(const GeometryShaderKey& other_key) const {
|
||||
return key == other_key.key;
|
||||
}
|
||||
bool operator!=(const GeometryShaderKey& other_key) const {
|
||||
return !(*this == other_key);
|
||||
}
|
||||
};
|
||||
|
||||
// Builds the geometry-shader cache key from the vertex/pixel shader
|
||||
// modifications. Returns false if no geometry shader is needed for the type.
|
||||
bool GetGeometryShaderKey(
|
||||
PipelineGeometryShader geometry_shader_type,
|
||||
DxbcShaderTranslator::Modification vertex_shader_modification,
|
||||
DxbcShaderTranslator::Modification pixel_shader_modification,
|
||||
GeometryShaderKey& key_out);
|
||||
|
||||
// Generates the DXBC geometry shader bytecode for the given key.
|
||||
void CreateDxbcGeometryShader(GeometryShaderKey key,
|
||||
std::vector<uint32_t>& shader_out);
|
||||
|
||||
} // namespace gpu
|
||||
} // namespace xe
|
||||
|
||||
#endif // XENIA_GPU_DXBC_GEOMETRY_SHADER_H_
|
||||
@@ -20,6 +20,24 @@ DxbcShader::DxbcShader(xenos::ShaderType shader_type, uint64_t ucode_data_hash,
|
||||
: Shader(shader_type, ucode_data_hash, ucode_dwords, ucode_dword_count,
|
||||
ucode_source_endian) {}
|
||||
|
||||
DxbcShader::TranslationMetadata DxbcShader::GetTranslationMetadata() const {
|
||||
TranslationMetadata metadata;
|
||||
metadata.texture_bindings = texture_bindings_;
|
||||
metadata.sampler_bindings = sampler_bindings_;
|
||||
metadata.used_texture_mask = used_texture_mask_;
|
||||
metadata.used_cbuffer_mask = used_cbuffer_mask_;
|
||||
metadata.fetch_constant_dword_mask = fetch_constant_dword_mask_;
|
||||
return metadata;
|
||||
}
|
||||
|
||||
void DxbcShader::SetTranslationMetadata(const TranslationMetadata& metadata) {
|
||||
texture_bindings_ = metadata.texture_bindings;
|
||||
sampler_bindings_ = metadata.sampler_bindings;
|
||||
used_texture_mask_ = metadata.used_texture_mask;
|
||||
used_cbuffer_mask_ = metadata.used_cbuffer_mask;
|
||||
fetch_constant_dword_mask_ = metadata.fetch_constant_dword_mask;
|
||||
}
|
||||
|
||||
Shader::Translation* DxbcShader::CreateTranslationInstance(
|
||||
uint64_t modification) {
|
||||
return new DxbcTranslation(*this, modification);
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#ifndef XENIA_GPU_DXBC_SHADER_H_
|
||||
#define XENIA_GPU_DXBC_SHADER_H_
|
||||
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <vector>
|
||||
|
||||
@@ -74,6 +75,31 @@ class DxbcShader : public Shader {
|
||||
return sampler_bindings_;
|
||||
}
|
||||
|
||||
static constexpr uint32_t kFetchConstantDwordCount =
|
||||
xenos::kTextureFetchConstantCount * 6;
|
||||
static constexpr uint32_t kFetchConstantDwordMaskWordCount =
|
||||
kFetchConstantDwordCount / 32;
|
||||
using FetchConstantDwordMask =
|
||||
std::array<uint32_t, kFetchConstantDwordMaskWordCount>;
|
||||
uint32_t GetUsedCbufferMaskAfterTranslation() const {
|
||||
return used_cbuffer_mask_;
|
||||
}
|
||||
const FetchConstantDwordMask& GetFetchConstantDwordMaskAfterTranslation()
|
||||
const {
|
||||
return fetch_constant_dword_mask_;
|
||||
}
|
||||
|
||||
struct TranslationMetadata {
|
||||
std::vector<TextureBinding> texture_bindings;
|
||||
std::vector<SamplerBinding> sampler_bindings;
|
||||
uint32_t used_texture_mask = 0;
|
||||
uint32_t used_cbuffer_mask = 0;
|
||||
FetchConstantDwordMask fetch_constant_dword_mask = {};
|
||||
};
|
||||
|
||||
TranslationMetadata GetTranslationMetadata() const;
|
||||
void SetTranslationMetadata(const TranslationMetadata& metadata);
|
||||
|
||||
protected:
|
||||
Translation* CreateTranslationInstance(uint64_t modification) override;
|
||||
|
||||
@@ -84,6 +110,8 @@ class DxbcShader : public Shader {
|
||||
std::vector<TextureBinding> texture_bindings_;
|
||||
std::vector<SamplerBinding> sampler_bindings_;
|
||||
uint32_t used_texture_mask_ = 0;
|
||||
uint32_t used_cbuffer_mask_ = 0;
|
||||
FetchConstantDwordMask fetch_constant_dword_mask_ = {};
|
||||
};
|
||||
|
||||
} // namespace gpu
|
||||
|
||||
@@ -136,6 +136,7 @@ void DxbcShaderTranslator::Reset() {
|
||||
cbuffer_index_bool_loop_constants_ = kBindingIndexUnallocated;
|
||||
cbuffer_index_fetch_constants_ = kBindingIndexUnallocated;
|
||||
cbuffer_index_descriptor_indices_ = kBindingIndexUnallocated;
|
||||
texture_fetch_constant_dword_mask_.fill(0);
|
||||
|
||||
system_constants_used_ = 0;
|
||||
|
||||
@@ -1368,6 +1369,44 @@ void DxbcShaderTranslator::PostTranslation() {
|
||||
DxbcShader* dxbc_shader = dynamic_cast<DxbcShader*>(&translation.shader());
|
||||
if (dxbc_shader && !dxbc_shader->bindings_setup_entered_.test_and_set(
|
||||
std::memory_order_relaxed)) {
|
||||
uint32_t used_cbuffer_mask = 0;
|
||||
auto mark_used_cbuffer = [&](CbufferRegister cbuffer_register) {
|
||||
used_cbuffer_mask |= uint32_t(1) << uint32_t(cbuffer_register);
|
||||
};
|
||||
mark_used_cbuffer(CbufferRegister::kSystemConstants);
|
||||
if (cbuffer_index_float_constants_ != kBindingIndexUnallocated) {
|
||||
mark_used_cbuffer(CbufferRegister::kFloatConstants);
|
||||
}
|
||||
if (cbuffer_index_bool_loop_constants_ != kBindingIndexUnallocated) {
|
||||
mark_used_cbuffer(CbufferRegister::kBoolLoopConstants);
|
||||
}
|
||||
if (cbuffer_index_fetch_constants_ != kBindingIndexUnallocated) {
|
||||
mark_used_cbuffer(CbufferRegister::kFetchConstants);
|
||||
}
|
||||
if (cbuffer_index_descriptor_indices_ != kBindingIndexUnallocated) {
|
||||
mark_used_cbuffer(CbufferRegister::kDescriptorIndices);
|
||||
}
|
||||
dxbc_shader->used_cbuffer_mask_ = used_cbuffer_mask;
|
||||
|
||||
dxbc_shader->fetch_constant_dword_mask_ =
|
||||
texture_fetch_constant_dword_mask_;
|
||||
const Shader::ConstantRegisterMap& constant_map =
|
||||
dxbc_shader->constant_register_map();
|
||||
for (uint32_t i = 0; i < xe::countof(constant_map.vertex_fetch_bitmap);
|
||||
++i) {
|
||||
uint32_t vfetch_bits_remaining = constant_map.vertex_fetch_bitmap[i];
|
||||
uint32_t bit_index;
|
||||
while (xe::bit_scan_forward(vfetch_bits_remaining, &bit_index)) {
|
||||
vfetch_bits_remaining = xe::clear_lowest_bit(vfetch_bits_remaining);
|
||||
const uint32_t vfetch_index = i * 32 + bit_index;
|
||||
const uint32_t dword_index = vfetch_index * 2;
|
||||
dxbc_shader->fetch_constant_dword_mask_[dword_index >> 5] |=
|
||||
uint32_t(1) << (dword_index & 31);
|
||||
dxbc_shader->fetch_constant_dword_mask_[(dword_index + 1) >> 5] |=
|
||||
uint32_t(1) << ((dword_index + 1) & 31);
|
||||
}
|
||||
}
|
||||
|
||||
dxbc_shader->texture_bindings_.clear();
|
||||
dxbc_shader->texture_bindings_.reserve(texture_bindings_.size());
|
||||
dxbc_shader->used_texture_mask_ = 0;
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
#ifndef XENIA_GPU_DXBC_SHADER_TRANSLATOR_H_
|
||||
#define XENIA_GPU_DXBC_SHADER_TRANSLATOR_H_
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
@@ -951,17 +952,41 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
if (cbuffer_index_fetch_constants_ == kBindingIndexUnallocated) {
|
||||
cbuffer_index_fetch_constants_ = cbuffer_count_++;
|
||||
}
|
||||
MarkTextureFetchConstantDwordUsed(fetch_constant_index * 6 +
|
||||
pair_index * 2);
|
||||
MarkTextureFetchConstantDwordUsed(fetch_constant_index * 6 +
|
||||
pair_index * 2 + 1);
|
||||
return GetTextureFetchConstantWordPairSource(fetch_constant_index,
|
||||
pair_index);
|
||||
}
|
||||
dxbc::Src RequestTextureFetchConstantWord(uint32_t fetch_constant_index,
|
||||
uint32_t word_index) {
|
||||
if (cbuffer_index_fetch_constants_ == kBindingIndexUnallocated) {
|
||||
cbuffer_index_fetch_constants_ = cbuffer_count_++;
|
||||
}
|
||||
MarkTextureFetchConstantDwordUsed(fetch_constant_index * 6 + word_index);
|
||||
return GetTextureFetchConstantWordPairSource(fetch_constant_index,
|
||||
word_index >> 1)
|
||||
.SelectFromSwizzled(word_index & 1);
|
||||
}
|
||||
|
||||
dxbc::Src GetTextureFetchConstantWordPairSource(uint32_t fetch_constant_index,
|
||||
uint32_t pair_index) const {
|
||||
uint32_t total_pair_index = fetch_constant_index * 3 + pair_index;
|
||||
return dxbc::Src::CB(cbuffer_index_fetch_constants_,
|
||||
uint32_t(CbufferRegister::kFetchConstants),
|
||||
total_pair_index >> 1,
|
||||
(total_pair_index & 1) ? 0b10101110 : 0b00000100);
|
||||
}
|
||||
dxbc::Src RequestTextureFetchConstantWord(uint32_t fetch_constant_index,
|
||||
uint32_t word_index) {
|
||||
return RequestTextureFetchConstantWordPair(fetch_constant_index,
|
||||
word_index >> 1)
|
||||
.SelectFromSwizzled(word_index & 1);
|
||||
void MarkTextureFetchConstantDwordUsed(uint32_t dword_index) {
|
||||
constexpr uint32_t kFetchConstantDwordCount =
|
||||
xenos::kTextureFetchConstantCount * 6;
|
||||
if (dword_index >= kFetchConstantDwordCount) {
|
||||
assert_always();
|
||||
return;
|
||||
}
|
||||
texture_fetch_constant_dword_mask_[dword_index >> 5] |=
|
||||
uint32_t(1) << (dword_index & 31);
|
||||
}
|
||||
|
||||
void KillPixel(bool condition, const dxbc::Src& condition_src,
|
||||
@@ -1084,6 +1109,8 @@ class DxbcShaderTranslator : public ShaderTranslator {
|
||||
uint32_t cbuffer_index_bool_loop_constants_;
|
||||
uint32_t cbuffer_index_fetch_constants_;
|
||||
uint32_t cbuffer_index_descriptor_indices_;
|
||||
std::array<uint32_t, xenos::kTextureFetchConstantCount * 6 / 32>
|
||||
texture_fetch_constant_dword_mask_;
|
||||
|
||||
struct SystemConstantRdef {
|
||||
const char* name;
|
||||
|
||||
@@ -93,6 +93,13 @@ DEFINE_int32(occlusion_query_fake_upper_threshold, 100,
|
||||
"GPU");
|
||||
DEFINE_bool(occlusion_query_log, false,
|
||||
"Log occlusion query lifetime and summary stats.", "GPU");
|
||||
DEFINE_bool(
|
||||
occlusion_query_fast_trust_report, false,
|
||||
"Prefer the current query report over cached fast mode values.\n"
|
||||
"Can improve occlusion accuracy in fast mode by reducing stale results,\n"
|
||||
"but may also regress occlusion culling in titles that already have\n"
|
||||
"issues with fast mode.",
|
||||
"GPU");
|
||||
DEFINE_int32(occlusion_query_querybatch_range, 0,
|
||||
"Range of fake sample count values to walk for titles using the\n"
|
||||
"D3D QueryBatch standard before wrapping back to\n"
|
||||
@@ -210,6 +217,53 @@ DEFINE_int32(anisotropic_override, -1,
|
||||
" 5 = Force 16x anisotropic filtering",
|
||||
"GPU");
|
||||
|
||||
DEFINE_bool(metal_shader_disk_cache, true,
|
||||
"Cache translated Metal shader artifacts and binding metadata in "
|
||||
"the packed Metal artifact store.",
|
||||
"Metal");
|
||||
|
||||
DEFINE_bool(metal_pipeline_binary_archive, true,
|
||||
"Use MTLBinaryArchive for Metal pipeline compilation caching. "
|
||||
"Requires store_shaders and a compatible OS/driver.",
|
||||
"Metal");
|
||||
|
||||
DEFINE_int32(
|
||||
metal_draw_ring_count, 128,
|
||||
"Metal per-command-buffer draw ring size (descriptor-table pages). "
|
||||
"Higher reduces ring churn but uses more memory.",
|
||||
"Metal");
|
||||
|
||||
DEFINE_bool(metal_backend_telemetry, true,
|
||||
"Log concise Metal backend decision counters for render encoder "
|
||||
"lifetime, resolve/transfer planning, bindless binding, and "
|
||||
"texture upload/load behavior.",
|
||||
"Metal");
|
||||
DEFINE_int32(metal_backend_telemetry_interval, 120,
|
||||
"Number of guest swaps between Metal backend telemetry summaries. "
|
||||
"Set to 0 to log only on shutdown.",
|
||||
"Metal");
|
||||
DEFINE_bool(
|
||||
metal_root_rebuild_detail_telemetry, false,
|
||||
"Collect expensive Metal root argument rebuild diagnostics, including slot "
|
||||
"change histograms and CBV resource identity details. Leave disabled for "
|
||||
"normal profiling.",
|
||||
"Metal");
|
||||
|
||||
DEFINE_bool(metal_constant_payload_cache, false,
|
||||
"Reuse identical Metal constant/descriptor payload uploads across "
|
||||
"draws within a frame. Disable to A/B hash/cache CPU cost against "
|
||||
"extra CBV uploads and root argument churn.",
|
||||
"Metal");
|
||||
|
||||
DEFINE_string(
|
||||
metal_residency_sets, "auto",
|
||||
"Use Metal residency sets for stable Metal allocations where supported.\n"
|
||||
" auto: Enable when the runtime exposes MTLResidencySet.\n"
|
||||
" true: Require creating a residency set, falling back only if creation "
|
||||
"fails.\n"
|
||||
" false: Use per-encoder useResource/useHeap only.",
|
||||
"Metal");
|
||||
|
||||
DEFINE_bool(
|
||||
ac6_ground_fix, false,
|
||||
"This fixes(hide) issues with black ground in AC6. Use only in AC6. "
|
||||
|
||||
@@ -37,6 +37,8 @@ DECLARE_int32(occlusion_query_fake_upper_threshold);
|
||||
|
||||
DECLARE_bool(occlusion_query_log);
|
||||
|
||||
DECLARE_bool(occlusion_query_fast_trust_report);
|
||||
|
||||
DECLARE_int32(occlusion_query_querybatch_range);
|
||||
|
||||
DECLARE_double(occlusion_query_saturation);
|
||||
@@ -68,6 +70,19 @@ DECLARE_bool(readback_resolve_half_pixel_offset);
|
||||
|
||||
DECLARE_bool(gpu_3d_to_2d_texture);
|
||||
|
||||
DECLARE_bool(metal_shader_disk_cache);
|
||||
DECLARE_bool(metal_pipeline_binary_archive);
|
||||
DECLARE_int32(metal_draw_ring_count);
|
||||
DECLARE_bool(metal_use_heaps);
|
||||
DECLARE_int32(metal_heap_min_bytes);
|
||||
DECLARE_bool(metal_texture_cache_use_private);
|
||||
DECLARE_bool(metal_texture_upload_via_blit);
|
||||
DECLARE_bool(metal_constant_payload_cache);
|
||||
DECLARE_string(metal_residency_sets);
|
||||
DECLARE_bool(metal_backend_telemetry);
|
||||
DECLARE_int32(metal_backend_telemetry_interval);
|
||||
DECLARE_bool(metal_root_rebuild_detail_telemetry);
|
||||
|
||||
DECLARE_bool(ac6_ground_fix);
|
||||
|
||||
#define XE_GPU_FINE_GRAINED_DRAW_SCOPES 1
|
||||
|
||||
@@ -83,6 +83,30 @@ GraphicsSystem::GraphicsSystem() : frame_limiter_worker_running_(false) {
|
||||
|
||||
GraphicsSystem::~GraphicsSystem() = default;
|
||||
|
||||
void GraphicsSystem::SetScaledAspectRatio(uint32_t x, uint32_t y) {
|
||||
uint32_t old_x = scaled_aspect_x_.exchange(x, std::memory_order_relaxed);
|
||||
uint32_t old_y = scaled_aspect_y_.exchange(y, std::memory_order_relaxed);
|
||||
if (old_x == x && old_y == y) {
|
||||
return;
|
||||
}
|
||||
|
||||
ScaledAspectRatioChangedCallback callback;
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(
|
||||
scaled_aspect_ratio_changed_callback_mutex_);
|
||||
callback = scaled_aspect_ratio_changed_callback_;
|
||||
}
|
||||
if (callback) {
|
||||
callback(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
void GraphicsSystem::SetScaledAspectRatioChangedCallback(
|
||||
ScaledAspectRatioChangedCallback callback) {
|
||||
std::lock_guard<std::mutex> lock(scaled_aspect_ratio_changed_callback_mutex_);
|
||||
scaled_aspect_ratio_changed_callback_ = std::move(callback);
|
||||
}
|
||||
|
||||
X_STATUS GraphicsSystem::Setup(cpu::Processor* processor,
|
||||
kernel::KernelState* kernel_state,
|
||||
ui::WindowedAppContext* app_context,
|
||||
|
||||
@@ -1,37 +1,195 @@
|
||||
add_library(xenia-gpu-metal STATIC
|
||||
dxbc_to_dxil_converter.cc
|
||||
dxbc_to_dxil_converter.h
|
||||
ir_runtime_impl.mm
|
||||
metal_backend_telemetry.cc
|
||||
metal_backend_telemetry.h
|
||||
metal_command_processor.cc
|
||||
metal_command_processor.h
|
||||
metal_direct_host_resolve.cc
|
||||
metal_geometry_shader.cc
|
||||
metal_geometry_shader.h
|
||||
metal_graphics_system.cc
|
||||
metal_graphics_system.h
|
||||
metal_heap_pool.cc
|
||||
metal_heap_pool.h
|
||||
metal_pipeline_cache.cc
|
||||
metal_pipeline_cache.h
|
||||
metal_primitive_processor.cc
|
||||
metal_primitive_processor.h
|
||||
metal_render_target_cache.cc
|
||||
metal_render_target_cache.h
|
||||
metal_shader.cc
|
||||
metal_shader.h
|
||||
metal_shader_cache.cc
|
||||
metal_shader_cache.h
|
||||
metal_shader_converter.cc
|
||||
metal_shader_converter.h
|
||||
metal_stage_compile_cache.cc
|
||||
metal_stage_compile_cache.h
|
||||
metal_shared_memory.cc
|
||||
metal_shared_memory.h
|
||||
metal_texture_cache.cc
|
||||
metal_texture_cache.h
|
||||
msl_bindings.h
|
||||
msl_shader.cc
|
||||
msl_shader.h
|
||||
msl_tess_factor_kernels.h
|
||||
)
|
||||
target_include_directories(xenia-gpu-metal PRIVATE
|
||||
${PROJECT_SOURCE_DIR}/third_party/glslang
|
||||
metal_upload_buffer_pool.cc
|
||||
metal_upload_buffer_pool.h
|
||||
metal_zpd_visibility_pool.cc
|
||||
metal_zpd_visibility_pool.h
|
||||
)
|
||||
target_link_libraries(xenia-gpu-metal PUBLIC
|
||||
xenia-base
|
||||
xenia-gpu
|
||||
xenia-ui-metal
|
||||
spirv-cross
|
||||
xenia-third-party-dxilconv
|
||||
xenia-third-party-metal-shader-converter
|
||||
"-framework Foundation"
|
||||
"-framework Metal"
|
||||
"-framework MetalKit"
|
||||
)
|
||||
target_link_libraries(xenia-gpu-metal PRIVATE glslang-spirv)
|
||||
xe_shader_rules_metal(xenia-gpu-metal ${CMAKE_CURRENT_SOURCE_DIR}/../shaders)
|
||||
if(APPLE)
|
||||
option(XE_METAL_SHADER_DEBUG_INFO
|
||||
"Embed MSL source + line tables in generated Metal metallibs so the compute/resolve shaders are viewable with per-line cost in the Xcode GPU trace (increases generated header/binary size)."
|
||||
ON)
|
||||
set(_metal_shader_args --msl)
|
||||
if(XE_METAL_SHADER_DEBUG_INFO)
|
||||
list(APPEND _metal_shader_args --metal-debug)
|
||||
endif()
|
||||
set(_direct_host_resolve_generated_root "${PROJECT_BINARY_DIR}/generated")
|
||||
set(_direct_host_resolve_bytecode_dir
|
||||
"${_direct_host_resolve_generated_root}/xenia/gpu/shaders/bytecode/metal")
|
||||
set(_direct_host_resolve_outputs)
|
||||
file(MAKE_DIRECTORY "${_direct_host_resolve_bytecode_dir}")
|
||||
|
||||
macro(_xe_metal_direct_host_resolve_variant id input)
|
||||
set(_variant_define_args)
|
||||
foreach(_define ${ARGN})
|
||||
list(APPEND _variant_define_args --define "${_define}")
|
||||
endforeach()
|
||||
set(_variant_out "${_direct_host_resolve_bytecode_dir}/${id}.h")
|
||||
set(_variant_dep "${_variant_out}.d")
|
||||
list(APPEND _direct_host_resolve_outputs "${_variant_out}")
|
||||
add_custom_command(
|
||||
OUTPUT "${_variant_out}"
|
||||
COMMAND $<TARGET_FILE:xenia-shader-cc>
|
||||
${_metal_shader_args}
|
||||
--identifier "${id}"
|
||||
${_variant_define_args}
|
||||
--depfile "${_variant_dep}"
|
||||
"${input}"
|
||||
"${_variant_out}"
|
||||
DEPENDS "${input}" xenia-shader-cc
|
||||
DEPFILE "${_variant_dep}"
|
||||
COMMENT "Metal: ${id}"
|
||||
VERBATIM
|
||||
)
|
||||
endmacro()
|
||||
|
||||
set(_resolve_host_color_entry
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../shaders/resolve_host_color_entry.xesli")
|
||||
set(_resolve_host_color_full_entry
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../shaders/resolve_host_color_full_entry.xesli")
|
||||
set(_resolve_host_depth_entry
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../shaders/resolve_host_depth_entry.xesli")
|
||||
|
||||
foreach(_source_uint 0 1)
|
||||
foreach(_bpp 32 64)
|
||||
foreach(_msaa 1 2 4)
|
||||
foreach(_scaled 0 1)
|
||||
set(_id "resolve_host_color")
|
||||
if(_source_uint)
|
||||
string(APPEND _id "_uint")
|
||||
endif()
|
||||
string(APPEND _id "_${_bpp}bpp_${_msaa}xmsaa")
|
||||
set(_defines
|
||||
"XE_RESOLVE_HOST_COLOR_BPP=${_bpp}"
|
||||
"XE_RESOLVE_HOST_COLOR_MSAA_SAMPLES=${_msaa}"
|
||||
"XE_RESOLVE_HOST_COLOR_SOURCE_UINT=${_source_uint}")
|
||||
if(_scaled)
|
||||
string(APPEND _id "_scaled")
|
||||
list(APPEND _defines "XE_RESOLVE_RESOLUTION_SCALED=1")
|
||||
endif()
|
||||
string(APPEND _id "_cs")
|
||||
_xe_metal_direct_host_resolve_variant(
|
||||
"${_id}" "${_resolve_host_color_entry}" ${_defines})
|
||||
endforeach()
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
foreach(_bpp 8 16 32 64 128)
|
||||
foreach(_msaa 1 2 4)
|
||||
foreach(_scaled 0 1)
|
||||
set(_id "resolve_host_color_full")
|
||||
if(_source_uint)
|
||||
string(APPEND _id "_uint")
|
||||
endif()
|
||||
string(APPEND _id "_${_bpp}bpp_${_msaa}xmsaa")
|
||||
set(_defines
|
||||
"XE_RESOLVE_HOST_COLOR_FULL_DEST_BPP=${_bpp}"
|
||||
"XE_RESOLVE_HOST_COLOR_MSAA_SAMPLES=${_msaa}"
|
||||
"XE_RESOLVE_HOST_COLOR_SOURCE_UINT=${_source_uint}")
|
||||
if(_scaled)
|
||||
string(APPEND _id "_scaled")
|
||||
list(APPEND _defines "XE_RESOLVE_RESOLUTION_SCALED=1")
|
||||
endif()
|
||||
string(APPEND _id "_cs")
|
||||
_xe_metal_direct_host_resolve_variant(
|
||||
"${_id}" "${_resolve_host_color_full_entry}" ${_defines})
|
||||
endforeach()
|
||||
endforeach()
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
foreach(_msaa 1 2 4)
|
||||
foreach(_scaled 0 1)
|
||||
set(_id "resolve_host_depth_32bpp_${_msaa}xmsaa")
|
||||
set(_defines "XE_RESOLVE_HOST_DEPTH_MSAA_SAMPLES=${_msaa}")
|
||||
if(_scaled)
|
||||
string(APPEND _id "_scaled")
|
||||
list(APPEND _defines "XE_RESOLVE_RESOLUTION_SCALED=1")
|
||||
endif()
|
||||
string(APPEND _id "_cs")
|
||||
_xe_metal_direct_host_resolve_variant(
|
||||
"${_id}" "${_resolve_host_depth_entry}" ${_defines})
|
||||
endforeach()
|
||||
endforeach()
|
||||
|
||||
add_custom_target(xenia-gpu-metal-direct-host-resolve-shaders
|
||||
DEPENDS ${_direct_host_resolve_outputs})
|
||||
add_dependencies(xenia-gpu-metal
|
||||
xenia-gpu-metal-direct-host-resolve-shaders)
|
||||
target_include_directories(xenia-gpu-metal BEFORE PRIVATE
|
||||
"${_direct_host_resolve_generated_root}")
|
||||
|
||||
set(_texture_upload_repack_shader
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../shaders/texture_upload_repack.metal")
|
||||
set(_texture_upload_repack_generated_root
|
||||
"${PROJECT_BINARY_DIR}/generated")
|
||||
set(_texture_upload_repack_out
|
||||
"${_texture_upload_repack_generated_root}/xenia/gpu/shaders/bytecode/metal/texture_upload_repack.h")
|
||||
set(_texture_upload_repack_dep "${_texture_upload_repack_out}.d")
|
||||
file(MAKE_DIRECTORY
|
||||
"${_texture_upload_repack_generated_root}/xenia/gpu/shaders/bytecode/metal")
|
||||
add_custom_command(
|
||||
OUTPUT "${_texture_upload_repack_out}"
|
||||
COMMAND $<TARGET_FILE:xenia-shader-cc>
|
||||
${_metal_shader_args}
|
||||
--depfile "${_texture_upload_repack_dep}"
|
||||
"${_texture_upload_repack_shader}"
|
||||
"${_texture_upload_repack_out}"
|
||||
DEPENDS "${_texture_upload_repack_shader}" xenia-shader-cc
|
||||
DEPFILE "${_texture_upload_repack_dep}"
|
||||
COMMENT "Metal: texture_upload_repack.metal"
|
||||
VERBATIM
|
||||
)
|
||||
add_custom_target(xenia-gpu-metal-texture-upload-repack-shader
|
||||
DEPENDS "${_texture_upload_repack_out}")
|
||||
add_dependencies(xenia-gpu-metal
|
||||
xenia-gpu-metal-texture-upload-repack-shader)
|
||||
target_include_directories(xenia-gpu-metal BEFORE PRIVATE
|
||||
"${_texture_upload_repack_generated_root}")
|
||||
set_source_files_properties("${_texture_upload_repack_shader}"
|
||||
PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||
target_sources(xenia-gpu-metal PRIVATE "${_texture_upload_repack_shader}")
|
||||
endif()
|
||||
xe_target_defaults(xenia-gpu-metal)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user