1
0
mirror of https://git.suyu.dev/suyu/suyu synced 2025-12-10 23:02:13 -06:00

9 Commits

Author SHA1 Message Date
Samuliak
5b9a9f3077 create vertex descriptor 2024-10-05 21:29:25 +02:00
Samuliak
0b12e22331 emit vertex attributes 2024-10-05 19:28:27 +02:00
Samuliak
5ace31b471 bind uniform buffers 2024-10-05 18:45:57 +02:00
Samuliak
1d39266956 fix: constant buffers not getting bound 2024-10-05 17:02:46 +02:00
Samuliak
dac285f6d9 bind buffers correctly 2024-10-05 16:27:00 +02:00
Samuliak
c24e3c23ff bind vertex buffers 2024-10-05 15:40:10 +02:00
Samuliak
89ff6909a9 bind stage buffers 2024-10-05 15:25:11 +02:00
Samuliak
a66708e362 rework the binding system 2024-10-05 15:06:58 +02:00
Samuliak
08dbc9bd8d prevent buffer view from crashing 2024-10-05 14:11:58 +02:00
14 changed files with 434 additions and 130 deletions

View File

@@ -1,7 +1,6 @@
// SPDX-FileCopyrightText: Copyright 2024 suyu Emulator Project
// SPDX-License-Identifier: GPL-2.0-or-later
#include <iostream>
#include <string_view>
#include "shader_recompiler/backend/msl/emit_msl_instructions.h"

View File

@@ -49,6 +49,7 @@ std::string_view InputArrayDecorator(Stage stage) {
*/
// TODO
/*
std::string OutputDecorator(Stage stage, u32 size) {
switch (stage) {
case Stage::TessellationControl:
@@ -57,6 +58,7 @@ std::string OutputDecorator(Stage stage, u32 size) {
return "";
}
}
*/
/*
// TODO
@@ -240,13 +242,13 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
stage_name = "vertex";
break;
case Stage::TessellationControl:
stage_name = "kernel";
stage_name = "INVALID";
break;
case Stage::TessellationEval:
stage_name = "vertex";
stage_name = "INVALID";
break;
case Stage::Geometry:
stage_name = "vertex";
stage_name = "INVALID";
break;
case Stage::Fragment:
stage_name = "fragment";
@@ -275,7 +277,11 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
if (!info.loads.Generic(index) || !runtime_info.previous_stage_stores.Generic(index)) {
continue;
}
DefineStageInOut(index, program.invocations, true);
const std::string qualifier{(stage == Stage::VertexA || stage == Stage::VertexB) ? "attribute(" : "user(locn"};
// TODO: uncomment
header += fmt::format("float4 attr{} [[{}{})]];\n"/*,
InterpDecorator(info.interpolation[index])*/, index/*,
InputArrayDecorator(stage)*/, qualifier, index);
has_stage_input = true;
}
for (size_t index = 0; index < info.uses_patches.size(); ++index) {
@@ -319,7 +325,7 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
}
for (size_t index = 0; index < IR::NUM_GENERICS; ++index) {
if (info.stores.Generic(index)) {
DefineStageInOut(index, program.invocations, false);
DefineStageOut(index, program.invocations);
}
}
header += "};\n";
@@ -342,6 +348,7 @@ EmitContext::EmitContext(IR::Program& program, Bindings& bindings, const Profile
void EmitContext::DefineInputs(Bindings& bindings) {
// Constant buffers
bindings.uniform_buffer = 0; // HACK
for (const auto& desc : info.constant_buffer_descriptors) {
const u32 cbuf_used_size{Common::DivCeil(info.constant_buffer_used_sizes[desc.index], 16U)};
const u32 cbuf_binding_size{info.uses_global_memory ? 0x1000U : cbuf_used_size};
@@ -362,6 +369,7 @@ void EmitContext::DefineInputs(Bindings& bindings) {
// TODO
// Storage space buffers
bindings.uniform_buffer = 8; // HACK
u32 index{};
for (const auto& desc : info.storage_buffers_descriptors) {
if (has_at_least_one_input)
@@ -435,14 +443,13 @@ void EmitContext::DefineInputs(Bindings& bindings) {
}
// TODO
void EmitContext::DefineStageInOut(size_t index, u32 invocations, bool is_input) {
const auto type{fmt::format("float{}", 4)};
void EmitContext::DefineStageOut(size_t index, u32 invocations) {
std::string name{fmt::format("attr{}", index)};
header += fmt::format("{} {}{} [[user(locn{})]];\n", type, name,
OutputDecorator(stage, invocations), index);
header += fmt::format("float4 {} [[user(locn{})]];\n", name/*,
OutputDecorator(stage, invocations)*/, index);
const GenericElementInfo element_info{
.name = (is_input ? "__in." : "__out.") + name,
.name = "__out." + name,
.first_element = 0,
.num_components = 4,
};
@@ -450,6 +457,7 @@ void EmitContext::DefineStageInOut(size_t index, u32 invocations, bool is_input)
}
void EmitContext::DefineHelperFunctions() {
// TODO: use MSL's extract_bits instead
header +=
"uint bitfieldExtract(uint value, int offset, int bits) {\nreturn (value >> offset) & "
"((1 << bits) - 1);\n}\n";

View File

@@ -162,7 +162,7 @@ public:
private:
// TODO: break down into smaller functions
void DefineInputs(Bindings& bindings);
void DefineStageInOut(size_t index, u32 invocations, bool is_input);
void DefineStageOut(size_t index, u32 invocations);
void DefineHelperFunctions();
void DefineConstants();
std::string DefineGlobalMemoryFunctions();

View File

@@ -145,4 +145,172 @@ inline size_t GetTextureBytesPerRow(VideoCore::Surface::PixelFormat pixel_format
format_info.bytes_per_block;
}
inline MTL::VertexFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) {
const MTL::VertexFormat format{([&]() {
switch (type) {
case Maxwell::VertexAttribute::Type::UnusedEnumDoNotUseBecauseItWillGoAway:
ASSERT_MSG(false, "Invalid vertex attribute type!");
break;
case Maxwell::VertexAttribute::Type::UNorm:
switch (size) {
case Maxwell::VertexAttribute::Size::Size_R8:
case Maxwell::VertexAttribute::Size::Size_A8:
return MTL::VertexFormatUCharNormalized;
case Maxwell::VertexAttribute::Size::Size_R8_G8:
case Maxwell::VertexAttribute::Size::Size_G8_R8:
return MTL::VertexFormatUChar2Normalized;
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
return MTL::VertexFormatUChar3Normalized;
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
return MTL::VertexFormatUChar4Normalized;
case Maxwell::VertexAttribute::Size::Size_R16:
return MTL::VertexFormatUShortNormalized;
case Maxwell::VertexAttribute::Size::Size_R16_G16:
return MTL::VertexFormatUShort2Normalized;
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
return MTL::VertexFormatUShort3Normalized;
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
return MTL::VertexFormatUShort4Normalized;
case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
return MTL::VertexFormatInvalid; // TODO: emulate
default:
break;
}
break;
case Maxwell::VertexAttribute::Type::SNorm:
switch (size) {
case Maxwell::VertexAttribute::Size::Size_R8:
case Maxwell::VertexAttribute::Size::Size_A8:
return MTL::VertexFormatCharNormalized;
case Maxwell::VertexAttribute::Size::Size_R8_G8:
case Maxwell::VertexAttribute::Size::Size_G8_R8:
return MTL::VertexFormatChar2Normalized;
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
return MTL::VertexFormatChar3Normalized;
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
return MTL::VertexFormatChar4Normalized;
case Maxwell::VertexAttribute::Size::Size_R16:
return MTL::VertexFormatShortNormalized;
case Maxwell::VertexAttribute::Size::Size_R16_G16:
return MTL::VertexFormatShort2Normalized;
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
return MTL::VertexFormatShort3Normalized;
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
return MTL::VertexFormatShort4Normalized;
case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
return MTL::VertexFormatInvalid; // TODO: emulate
default:
break;
}
break;
case Maxwell::VertexAttribute::Type::UInt:
case Maxwell::VertexAttribute::Type::UScaled:
switch (size) {
case Maxwell::VertexAttribute::Size::Size_R8:
case Maxwell::VertexAttribute::Size::Size_A8:
return MTL::VertexFormatUChar;
case Maxwell::VertexAttribute::Size::Size_R8_G8:
case Maxwell::VertexAttribute::Size::Size_G8_R8:
return MTL::VertexFormatUChar2;
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
return MTL::VertexFormatUChar3;
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
return MTL::VertexFormatUChar4;
case Maxwell::VertexAttribute::Size::Size_R16:
return MTL::VertexFormatUShort;
case Maxwell::VertexAttribute::Size::Size_R16_G16:
return MTL::VertexFormatUShort2;
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
return MTL::VertexFormatUShort3;
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
return MTL::VertexFormatUShort4;
case Maxwell::VertexAttribute::Size::Size_R32:
return MTL::VertexFormatUInt;
case Maxwell::VertexAttribute::Size::Size_R32_G32:
return MTL::VertexFormatUInt2;
case Maxwell::VertexAttribute::Size::Size_R32_G32_B32:
return MTL::VertexFormatUInt3;
case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32:
return MTL::VertexFormatUInt4;
case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
return MTL::VertexFormatInvalid; // TODO: emulate
default:
break;
}
break;
case Maxwell::VertexAttribute::Type::SInt:
case Maxwell::VertexAttribute::Type::SScaled:
switch (size) {
case Maxwell::VertexAttribute::Size::Size_R8:
case Maxwell::VertexAttribute::Size::Size_A8:
return MTL::VertexFormatChar;
case Maxwell::VertexAttribute::Size::Size_R8_G8:
case Maxwell::VertexAttribute::Size::Size_G8_R8:
return MTL::VertexFormatChar2;
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8:
return MTL::VertexFormatChar3;
case Maxwell::VertexAttribute::Size::Size_R8_G8_B8_A8:
case Maxwell::VertexAttribute::Size::Size_X8_B8_G8_R8:
return MTL::VertexFormatChar4;
case Maxwell::VertexAttribute::Size::Size_R16:
return MTL::VertexFormatShort;
case Maxwell::VertexAttribute::Size::Size_R16_G16:
return MTL::VertexFormatShort2;
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
return MTL::VertexFormatShort3;
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
return MTL::VertexFormatShort4;
case Maxwell::VertexAttribute::Size::Size_R32:
return MTL::VertexFormatInt;
case Maxwell::VertexAttribute::Size::Size_R32_G32:
return MTL::VertexFormatInt2;
case Maxwell::VertexAttribute::Size::Size_R32_G32_B32:
return MTL::VertexFormatInt3;
case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32:
return MTL::VertexFormatInt4;
case Maxwell::VertexAttribute::Size::Size_A2_B10_G10_R10:
return MTL::VertexFormatInvalid; // TODO: emulate
default:
break;
}
break;
case Maxwell::VertexAttribute::Type::Float:
switch (size) {
case Maxwell::VertexAttribute::Size::Size_R16:
return MTL::VertexFormatHalf;
case Maxwell::VertexAttribute::Size::Size_R16_G16:
return MTL::VertexFormatHalf2;
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16:
return MTL::VertexFormatHalf3;
case Maxwell::VertexAttribute::Size::Size_R16_G16_B16_A16:
return MTL::VertexFormatHalf4;
case Maxwell::VertexAttribute::Size::Size_R32:
return MTL::VertexFormatFloat;
case Maxwell::VertexAttribute::Size::Size_R32_G32:
return MTL::VertexFormatFloat2;
case Maxwell::VertexAttribute::Size::Size_R32_G32_B32:
return MTL::VertexFormatFloat3;
case Maxwell::VertexAttribute::Size::Size_R32_G32_B32_A32:
return MTL::VertexFormatFloat4;
case Maxwell::VertexAttribute::Size::Size_B10_G11_R11:
return MTL::VertexFormatInvalid; // TODO: emulate
default:
break;
}
break;
}
return MTL::VertexFormatInvalid;
})()};
if (format == MTL::VertexFormatInvalid) {
UNIMPLEMENTED_MSG("Unimplemented vertex format of type={} and size={}", type, size);
}
return format;
}
} // namespace Metal::MaxwellToMTL

View File

@@ -27,7 +27,8 @@ BufferView::BufferView(MTL::Buffer* buffer_, size_t offset_, size_t size_,
: buffer{buffer_->retain()}, offset{offset_}, size{size_}, format{format_} {}
BufferView::~BufferView() {
buffer->release();
// TODO: uncomment
// buffer->release();
}
Buffer::Buffer(BufferCacheRuntime& runtime, VideoCommon::NullBufferParams null_params)
@@ -93,19 +94,25 @@ void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 fir
// TODO: bind quad index buffer
}
void BufferCacheRuntime::BindVertexBuffer(size_t stage, u32 index, MTL::Buffer* buffer, u32 offset,
u32 size, u32 stride) {
// TODO: use stride
BindBuffer(stage, MAX_METAL_BUFFERS - index - 1, buffer, offset, size);
void BufferCacheRuntime::BindVertexBuffer(u32 index, MTL::Buffer* buffer, u32 offset, u32 size,
u32 stride) {
// TODO: use stride?
BindBuffer(0, MAX_METAL_BUFFERS - index - 1, buffer, offset, size);
}
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
// TODO: implement
for (u32 index = 0; index < bindings.buffers.size(); ++index) {
auto handle = bindings.buffers[index]->Handle();
if (handle) {
// TODO: set stride?
BindVertexBuffer(index, handle, bindings.offsets[index], bindings.sizes[index], 0);
}
}
}
void BufferCacheRuntime::BindBuffer(size_t stage, u32 binding_index, MTL::Buffer* buffer,
u32 offset, u32 size) {
command_recorder.SetBuffer(stage, buffer, binding_index, offset);
command_recorder.SetBuffer(stage, buffer, offset, binding_index);
}
void BufferCacheRuntime::ReserveNullBuffer() {
@@ -115,7 +122,7 @@ void BufferCacheRuntime::ReserveNullBuffer() {
}
MTL::Buffer* BufferCacheRuntime::CreateNullBuffer() {
return CreatePrivateBuffer(device, NULL_BUFFER_SIZE);
return CreatePrivateBuffer(device, NULL_BUFFER_SIZE * 2);
}
} // namespace Metal

View File

@@ -22,7 +22,7 @@ struct BufferView {
VideoCore::Surface::PixelFormat format_ = VideoCore::Surface::PixelFormat::Invalid);
~BufferView();
MTL::Buffer* buffer = nil;
MTL::Buffer* buffer = nullptr;
size_t offset{};
size_t size{};
VideoCore::Surface::PixelFormat format{};
@@ -36,7 +36,7 @@ public:
[[nodiscard]] BufferView View(u32 offset, u32 size, VideoCore::Surface::PixelFormat format);
void MarkUsage(u64 offset, u64 size) noexcept {
// TODO: track usage
// TODO: track usage?
}
[[nodiscard]] MTL::Buffer* Handle() const noexcept {
@@ -110,7 +110,7 @@ public:
void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count);
void BindVertexBuffer(size_t stage, u32 index, MTL::Buffer* buffer, u32 offset, u32 size,
void BindVertexBuffer(u32 index, MTL::Buffer* buffer, u32 offset, u32 size,
u32 stride);
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
@@ -122,8 +122,10 @@ public:
void BindTransformFeedbackBuffers(VideoCommon::HostBindings<Buffer>& bindings) {}
std::span<u8> BindMappedUniformBuffer(size_t stage, u32 binding_index, u32 size) {
// TODO: just set bytes in case the size is <= 4KB?
const StagingBufferRef ref = staging_pool.Request(size, MemoryUsage::Upload);
BindBuffer(stage, binding_index, ref.buffer, static_cast<u32>(ref.offset), size);
return ref.mapped_span;
}
@@ -137,7 +139,7 @@ public:
void BindStorageBuffer(size_t stage, u32 binding_index, MTL::Buffer* buffer, u32 offset,
u32 size, [[maybe_unused]] bool is_written) {
BindBuffer(stage, binding_index, buffer, offset, size);
BindBuffer(stage, binding_index + 8, buffer, offset, size); // HACK: offset by 8
}
// TODO: implement
@@ -159,8 +161,8 @@ private:
StagingBufferPool& staging_pool;
// Common buffers
MTL::Buffer* null_buffer = nil;
MTL::Buffer* quad_index_buffer = nil;
MTL::Buffer* null_buffer = nullptr;
MTL::Buffer* quad_index_buffer = nullptr;
};
struct BufferCacheParams {

View File

@@ -20,6 +20,7 @@ void CommandRecorder::BeginOrContinueRenderPass(MTL::RenderPassDescriptor* rende
render_state.render_pass = render_pass;
should_reset_bound_resources = true;
}
const auto bind_resources{[&](size_t stage) {
// Buffers
for (u8 i = 0; i < MAX_BUFFERS; i++) {
@@ -28,17 +29,18 @@ void CommandRecorder::BeginOrContinueRenderPass(MTL::RenderPassDescriptor* rende
(bound_buffer.needs_update || should_reset_bound_resources)) {
switch (stage) {
case 0:
GetRenderCommandEncoderUnchecked()->setVertexBuffer(bound_buffer.buffer, i,
bound_buffer.offset);
GetRenderCommandEncoderUnchecked()->setVertexBuffer(bound_buffer.buffer,
bound_buffer.offset, i);
break;
case 4:
GetRenderCommandEncoderUnchecked()->setFragmentBuffer(bound_buffer.buffer, i,
bound_buffer.offset);
GetRenderCommandEncoderUnchecked()->setFragmentBuffer(bound_buffer.buffer,
bound_buffer.offset, i);
break;
}
bound_buffer.needs_update = false;
}
}
// Textures
for (u8 i = 0; i < MAX_TEXTURES; i++) {
auto& bound_texture = render_state.textures[stage][i];
@@ -56,6 +58,7 @@ void CommandRecorder::BeginOrContinueRenderPass(MTL::RenderPassDescriptor* rende
bound_texture.needs_update = false;
}
}
// Sampler states
for (u8 i = 0; i < MAX_SAMPLERS; i++) {
auto& bound_sampler_state = render_state.sampler_states[stage][i];
@@ -78,20 +81,6 @@ void CommandRecorder::BeginOrContinueRenderPass(MTL::RenderPassDescriptor* rende
bind_resources(0);
bind_resources(4);
if (should_reset_bound_resources) {
for (size_t stage = 0; stage < 5; stage++) {
for (u8 i = 0; i < MAX_BUFFERS; i++) {
render_state.buffers[stage][i].buffer = nullptr;
}
for (u8 i = 0; i < MAX_TEXTURES; i++) {
render_state.textures[stage][i].texture = nullptr;
}
for (u8 i = 0; i < MAX_SAMPLERS; i++) {
render_state.sampler_states[stage][i].sampler_state = nullptr;
}
}
}
}
void CommandRecorder::RequireComputeEncoder() {

View File

@@ -53,9 +53,6 @@ struct RenderState {
BoundIndexBuffer bound_index_buffer;
};
// TODO: whenever a render pass gets interrupted by either a compute or blit command and application
// then tries to perform a render command, begin the same render pass, but with all load actions set
// to "load"
class CommandRecorder {
using PrimitiveTopology = Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology;
using IndexFormat = Tegra::Engines::Maxwell3D::Regs::IndexFormat;
@@ -117,7 +114,7 @@ public:
}
}
inline void SetBuffer(size_t stage, MTL::Buffer* buffer, size_t index, size_t offset) {
inline void SetBuffer(size_t stage, MTL::Buffer* buffer, size_t offset, size_t index) {
auto& bound_buffer = render_state.buffers[stage][index];
if (buffer != bound_buffer.buffer) {
bound_buffer = {true, buffer, offset};

View File

@@ -18,6 +18,7 @@
#include "video_core/shader_notify.h"
#include "video_core/texture_cache/texture_cache.h"
#include "video_core/texture_cache/texture_cache_base.h"
#include "video_core/renderer_metal/maxwell_to_mtl.h"
namespace Metal {
namespace {
@@ -46,6 +47,8 @@ GraphicsPipeline::GraphicsPipeline(const Device& device_, CommandRecorder& comma
continue;
}
stage_infos[stage] = *info;
enabled_uniform_buffer_masks[stage] = info->constant_buffer_mask;
std::ranges::copy(info->constant_buffer_used_sizes, uniform_buffer_sizes[stage].begin());
}
Validate();
// TODO: is the framebuffer available by this time?
@@ -54,22 +57,36 @@ GraphicsPipeline::GraphicsPipeline(const Device& device_, CommandRecorder& comma
LOG_DEBUG(Render_Metal, "framebuffer not available");
return;
}
MakePipeline(framebuffer->GetHandle());
}
void GraphicsPipeline::Configure(bool is_indexed) {
texture_cache.SynchronizeGraphicsDescriptors();
std::array<VideoCommon::ImageViewInOut, 32> views;
std::array<VideoCommon::SamplerId, 32> samplers;
size_t view_index{};
size_t sampler_index{};
struct {
std::array<VideoCommon::ImageViewInOut, 32> views;
size_t view_index{};
} all_views[5];
struct {
std::array<VideoCommon::SamplerId, 32> samplers;
size_t sampler_index{};
} all_samplers[5];
// Find resources
const auto& regs{maxwell3d->regs};
const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding};
const auto configure_stage{[&](u32 stage) {
const Shader::Info& info{stage_infos[stage]};
std::array<VideoCommon::ImageViewInOut, 32>& views{all_views[stage].views};
std::array<VideoCommon::SamplerId, 32>& samplers{all_samplers[stage].samplers};
size_t& view_index{all_views[stage].view_index};
size_t& sampler_index{all_samplers[stage].sampler_index};
texture_cache.SynchronizeGraphicsDescriptors();
buffer_cache.SetUniformBuffersState(enabled_uniform_buffer_masks, &uniform_buffer_sizes);
buffer_cache.UnbindGraphicsStorageBuffers(stage);
size_t ssbo_index{};
for (const auto& desc : info.storage_buffers_descriptors) {
@@ -141,53 +158,6 @@ void GraphicsPipeline::Configure(bool is_indexed) {
for (const auto& desc : info.image_descriptors) {
add_image(desc, desc.is_written);
}
/*
const auto& cbufs{maxwell3d->state.shader_stages[stage].const_buffers};
const auto read_handle{[&](const auto& desc, u32 index) {
ASSERT(cbufs[desc.cbuf_index].enabled);
const u32 index_offset{index << desc.size_shift};
const u32 offset{desc.cbuf_offset + index_offset};
const GPUVAddr addr{cbufs[desc.cbuf_index].address + offset};
if constexpr (std::is_same_v<decltype(desc), const Shader::TextureDescriptor&> ||
std::is_same_v<decltype(desc), const Shader::TextureBufferDescriptor&>) {
if (desc.has_secondary) {
ASSERT(cbufs[desc.secondary_cbuf_index].enabled);
const u32 second_offset{desc.secondary_cbuf_offset + index_offset};
const GPUVAddr separate_addr{cbufs[desc.secondary_cbuf_index].address +
second_offset};
const u32 lhs_raw{gpu_memory->Read<u32>(addr) << desc.shift_left};
const u32 rhs_raw{gpu_memory->Read<u32>(separate_addr)
<< desc.secondary_shift_left};
const u32 raw{lhs_raw | rhs_raw};
return TexturePair(raw, false);
}
}
auto a = gpu_memory->Read<u32>(addr);
// HACK: this particular texture breaks SMO
if (a == 310378931)
a = 310378932;
return TexturePair(a, false);
}};
const Shader::Info& info{stage_infos[stage]};
std::array<VideoCommon::ImageViewInOut, 32> views;
std::array<VideoCommon::SamplerId, 32> samplers;
size_t view_index{};
size_t sampler_index{};
for (const auto& desc : info.texture_descriptors) {
for (u32 index = 0; index < desc.count; ++index) {
const auto handle{read_handle(desc, index)};
views[view_index++] = {handle.first};
VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)};
samplers[sampler_index++] = sampler;
}
}
*/
}};
configure_stage(0);
@@ -196,19 +166,34 @@ void GraphicsPipeline::Configure(bool is_indexed) {
buffer_cache.UpdateGraphicsBuffers(is_indexed);
buffer_cache.BindHostGeometryBuffers(is_indexed);
texture_cache.FillGraphicsImageViews<true>(std::span(views.data(), view_index));
// Bind resources
const auto bind_stage_resources{[&](u32 stage) {
std::array<VideoCommon::ImageViewInOut, 32>& views{all_views[stage].views};
std::array<VideoCommon::SamplerId, 32>& samplers{all_samplers[stage].samplers};
const size_t& view_index{all_views[stage].view_index};
const size_t& sampler_index{all_samplers[stage].sampler_index};
// HACK: try to find a texture that we can bind
const VideoCommon::ImageViewInOut* views_it{views.data()};
const VideoCommon::SamplerId* samplers_it{samplers.data()};
// Buffers
buffer_cache.BindHostStageBuffers(stage);
ImageView& image_view{texture_cache.GetImageView(views_it->id)};
Sampler& sampler{texture_cache.GetSampler(*samplers_it)};
// Textures
texture_cache.FillGraphicsImageViews<true>(std::span(views.data(), view_index));
for (u8 i = 0; i < view_index; i++) {
const VideoCommon::ImageViewInOut& view{views[i]};
ImageView& image_view{texture_cache.GetImageView(view.id)};
command_recorder.SetTexture(stage, image_view.GetHandle(), i);
}
command_recorder.SetTexture(4, image_view.GetHandle(), 0);
command_recorder.SetSamplerState(4, sampler.GetHandle(), 0);
// Samplers
for (u8 i = 0; i < sampler_index; i++) {
const VideoCommon::SamplerId& sampler_id{samplers[i]};
Sampler& sampler{texture_cache.GetSampler(sampler_id)};
command_recorder.SetSamplerState(stage, sampler.GetHandle(), i);
}
}};
bind_stage_resources(0);
bind_stage_resources(4);
// Begin render pass
texture_cache.UpdateRenderTargets(false);
@@ -218,16 +203,47 @@ void GraphicsPipeline::Configure(bool is_indexed) {
}
command_recorder.BeginOrContinueRenderPass(framebuffer->GetHandle());
MakePipeline(framebuffer->GetHandle());
command_recorder.SetRenderPipelineState(pipeline_state);
}
void GraphicsPipeline::MakePipeline(MTL::RenderPassDescriptor* render_pass) {
MTL::RenderPipelineDescriptor* pipeline_descriptor =
const auto& regs{maxwell3d->regs};
// Shader stages
MTL::RenderPipelineDescriptor* desc =
MTL::RenderPipelineDescriptor::alloc()->init();
pipeline_descriptor->setVertexFunction(functions[0]);
pipeline_descriptor->setFragmentFunction(functions[4]);
// pipeline_descriptor->setVertexDescriptor(vertex_descriptor);
// TODO: get the attachment count from render pass descriptor
desc->setVertexFunction(functions[0]);
desc->setFragmentFunction(functions[4]);
// Vertex descriptor
MTL::VertexDescriptor* vertex_descriptor = MTL::VertexDescriptor::alloc()->init();
for (size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
const auto& array = regs.vertex_streams[index];
if (!array.enable)
continue;
ASSERT(index < MAX_BUFFERS);
// TODO: instancing
auto layout = vertex_descriptor->layouts()->object(index);
layout->setStride(array.stride.Value());
}
for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
const auto& input = regs.vertex_attrib_format[index];
// TODO: doesn't this need some special handling?
if (input.constant)
continue;
auto attribute = vertex_descriptor->attributes()->object(index);
attribute->setBufferIndex(input.buffer);
attribute->setOffset(input.offset);
attribute->setFormat(MaxwellToMTL::VertexFormat(input.type.Value(), input.size.Value()));
}
desc->setVertexDescriptor(vertex_descriptor);
vertex_descriptor->release();
// Color attachments
for (u32 index = 0; index < NUM_RT; index++) {
auto* render_pass_attachment = render_pass->colorAttachments()->object(index);
// TODO: is this the correct way to check if the attachment is valid?
@@ -235,13 +251,14 @@ void GraphicsPipeline::MakePipeline(MTL::RenderPassDescriptor* render_pass) {
continue;
}
auto* color_attachment = pipeline_descriptor->colorAttachments()->object(index);
auto* color_attachment = desc->colorAttachments()->object(index);
color_attachment->setPixelFormat(render_pass_attachment->texture()->pixelFormat());
// TODO: provide blend information
}
NS::Error* error = nullptr;
pipeline_state = device.GetDevice()->newRenderPipelineState(pipeline_descriptor, &error);
pipeline_state = device.GetDevice()->newRenderPipelineState(desc, &error);
desc->release();
if (error) {
LOG_ERROR(Render_Metal, "failed to create pipeline state: {}",
error->description()->cString(NS::ASCIIStringEncoding));

View File

@@ -114,9 +114,11 @@ private:
std::array<MTL::Function*, NUM_STAGES> functions;
std::array<Shader::Info, NUM_STAGES> stage_infos;
// VideoCommon::UniformBufferSizes uniform_buffer_sizes{};
std::array<u32, 5> enabled_uniform_buffer_masks{};
VideoCommon::UniformBufferSizes uniform_buffer_sizes{};
// u32 num_textures{};
// TODO: cache pipelines if state changes
MTL::RenderPipelineState* pipeline_state{nullptr};
};

View File

@@ -56,9 +56,124 @@ Shader::RuntimeInfo MakeRuntimeInfo(std::span<const Shader::IR::Program> program
const GraphicsPipelineCacheKey& key,
const Shader::IR::Program& program,
const Shader::IR::Program* previous_program) {
Shader::RuntimeInfo info{};
// TODO: fill in the runtime info
Shader::RuntimeInfo info;
if (previous_program) {
info.previous_stage_stores = previous_program->info.stores;
info.previous_stage_legacy_stores_mapping = previous_program->info.legacy_stores_mapping;
if (previous_program->is_geometry_passthrough) {
info.previous_stage_stores.mask |= previous_program->info.passthrough.mask;
}
} else {
info.previous_stage_stores.mask.set();
}
// TODO: uncomment
/*
const Shader::Stage stage{program.stage};
const bool has_geometry{key.unique_hashes[4] != 0 && !programs[4].is_geometry_passthrough};
const bool gl_ndc{key.state.ndc_minus_one_to_one != 0};
const float point_size{Common::BitCast<float>(key.state.point_size)};
switch (stage) {
case Shader::Stage::VertexB:
if (!has_geometry) {
if (key.state.topology == Maxwell::PrimitiveTopology::Points) {
info.fixed_state_point_size = point_size;
}
if (key.state.xfb_enabled) {
auto [varyings, count] =
VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state);
info.xfb_varyings = varyings;
info.xfb_count = count;
}
info.convert_depth_mode = gl_ndc;
}
if (key.state.dynamic_vertex_input) {
for (size_t index = 0; index < Maxwell::NumVertexAttributes; ++index) {
info.generic_input_types[index] = AttributeType(key.state, index);
}
} else {
std::ranges::transform(key.state.attributes, info.generic_input_types.begin(),
&CastAttributeType);
}
break;
case Shader::Stage::TessellationEval:
info.tess_clockwise = key.state.tessellation_clockwise != 0;
info.tess_primitive = [&key] {
const u32 raw{key.state.tessellation_primitive.Value()};
switch (static_cast<Maxwell::Tessellation::DomainType>(raw)) {
case Maxwell::Tessellation::DomainType::Isolines:
return Shader::TessPrimitive::Isolines;
case Maxwell::Tessellation::DomainType::Triangles:
return Shader::TessPrimitive::Triangles;
case Maxwell::Tessellation::DomainType::Quads:
return Shader::TessPrimitive::Quads;
}
ASSERT(false);
return Shader::TessPrimitive::Triangles;
}();
info.tess_spacing = [&] {
const u32 raw{key.state.tessellation_spacing};
switch (static_cast<Maxwell::Tessellation::Spacing>(raw)) {
case Maxwell::Tessellation::Spacing::Integer:
return Shader::TessSpacing::Equal;
case Maxwell::Tessellation::Spacing::FractionalOdd:
return Shader::TessSpacing::FractionalOdd;
case Maxwell::Tessellation::Spacing::FractionalEven:
return Shader::TessSpacing::FractionalEven;
}
ASSERT(false);
return Shader::TessSpacing::Equal;
}();
break;
case Shader::Stage::Geometry:
if (program.output_topology == Shader::OutputTopology::PointList) {
info.fixed_state_point_size = point_size;
}
if (key.state.xfb_enabled != 0) {
auto [varyings, count] =
VideoCommon::MakeTransformFeedbackVaryings(key.state.xfb_state);
info.xfb_varyings = varyings;
info.xfb_count = count;
}
info.convert_depth_mode = gl_ndc;
break;
case Shader::Stage::Fragment:
info.alpha_test_func = MaxwellToCompareFunction(
key.state.UnpackComparisonOp(key.state.alpha_test_func.Value()));
info.alpha_test_reference = Common::BitCast<float>(key.state.alpha_test_ref);
break;
default:
break;
}
switch (key.state.topology) {
case Maxwell::PrimitiveTopology::Points:
info.input_topology = Shader::InputTopology::Points;
break;
case Maxwell::PrimitiveTopology::Lines:
case Maxwell::PrimitiveTopology::LineLoop:
case Maxwell::PrimitiveTopology::LineStrip:
info.input_topology = Shader::InputTopology::Lines;
break;
case Maxwell::PrimitiveTopology::Triangles:
case Maxwell::PrimitiveTopology::TriangleStrip:
case Maxwell::PrimitiveTopology::TriangleFan:
case Maxwell::PrimitiveTopology::Quads:
case Maxwell::PrimitiveTopology::QuadStrip:
case Maxwell::PrimitiveTopology::Polygon:
case Maxwell::PrimitiveTopology::Patches:
info.input_topology = Shader::InputTopology::Triangles;
break;
case Maxwell::PrimitiveTopology::LinesAdjacency:
case Maxwell::PrimitiveTopology::LineStripAdjacency:
info.input_topology = Shader::InputTopology::LinesAdjacency;
break;
case Maxwell::PrimitiveTopology::TrianglesAdjacency:
case Maxwell::PrimitiveTopology::TriangleStripAdjacency:
info.input_topology = Shader::InputTopology::TrianglesAdjacency;
break;
}
info.force_early_z = key.state.early_z != 0;
info.y_negate = key.state.y_negate != 0;
*/
return info;
}
@@ -231,7 +346,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
auto hash = key.Hash();
LOG_INFO(Render_Metal, "0x{:016x}", hash);
// Translate shaders to spirv
// Translate shaders
size_t env_index{0};
std::array<Shader::IR::Program, Maxwell::MaxShaderProgram> programs;
@@ -268,7 +383,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
ConvertLegacyToGeneric(program, runtime_info);
const std::string code{EmitMSL(profile, runtime_info, program, binding)};
// HACK
std::cout << "SHADER INDEX: " << index - 1 << std::endl;
std::cout << "SHADER INDEX: " << stage_index << std::endl;
std::cout << code << std::endl;
MTL::CompileOptions* compile_options = MTL::CompileOptions::alloc()->init();
NS::Error* error = nullptr;
@@ -289,6 +404,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
}
// HACK: create hardcoded shaders
/*
MTL::CompileOptions* compile_options = MTL::CompileOptions::alloc()->init();
NS::Error* error = nullptr;
MTL::Library* library = device.GetDevice()->newLibrary(NS::String::string(
@@ -330,6 +446,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
functions[0] = library->newFunction(NS::String::string("vertexMain", NS::ASCIIStringEncoding));
functions[4] =
library->newFunction(NS::String::string("fragmentMain", NS::ASCIIStringEncoding));
*/
return std::make_unique<GraphicsPipeline>(device, command_recorder, key, buffer_cache,
texture_cache, &shader_notify, functions, infos);

View File

@@ -124,11 +124,11 @@ void RasterizerMetal::Query(GPUVAddr gpu_addr, VideoCommon::QueryType type,
void RasterizerMetal::BindGraphicsUniformBuffer(size_t stage, u32 index, GPUVAddr gpu_addr,
u32 size) {
LOG_DEBUG(Render_Metal, "called");
buffer_cache.BindGraphicsUniformBuffer(stage, index, gpu_addr, size);
}
void RasterizerMetal::DisableGraphicsUniformBuffer(size_t stage, u32 index) {
LOG_DEBUG(Render_Metal, "called");
buffer_cache.DisableGraphicsUniformBuffer(stage, index);
}
void RasterizerMetal::FlushAll() {

View File

@@ -2,7 +2,6 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <algorithm>
#include <iostream>
#include <utility>
#include <vector>
@@ -30,7 +29,8 @@ StagingBuffer::StagingBuffer(MTL::Buffer* buffer_, std::span<u8> mapped_span_)
: buffer{buffer_->retain()}, mapped_span{mapped_span_} {}
StagingBuffer::~StagingBuffer() {
buffer->release();
// TODO: uncomment
//buffer->release();
}
StagingBufferRef StagingBuffer::Ref() const noexcept {

View File

@@ -175,15 +175,13 @@ void Framebuffer::CreateRenderPassDescriptor(TextureCacheRuntime& runtime,
}
// TODO: don't use index as attachment index
auto color_attachment = render_pass->colorAttachments()->object(index);
color_attachment->setClearColor(MTL::ClearColor::Make(0.5, 1.0, 0.0, 1.0));
color_attachment->setLoadAction(MTL::LoadActionClear);
color_attachment->setLoadAction(MTL::LoadActionLoad);
color_attachment->setStoreAction(MTL::StoreActionStore);
color_attachment->setTexture(color_buffer->GetHandle());
}
if (depth_buffer) {
auto depth_attachment = render_pass->depthAttachment();
depth_attachment->setClearDepth(1.0);
depth_attachment->setLoadAction(MTL::LoadActionClear);
depth_attachment->setLoadAction(MTL::LoadActionLoad);
depth_attachment->setStoreAction(MTL::StoreActionStore);
depth_attachment->setTexture(depth_buffer->GetHandle());
}