mirror of
https://git.suyu.dev/suyu/suyu
synced 2024-12-25 19:02:45 -06:00
rework the binding system
This commit is contained in:
parent
49f60adeb2
commit
3a51a8de2e
7 changed files with 320 additions and 184 deletions
|
@ -22,15 +22,6 @@ MTL::Buffer* CreatePrivateBuffer(const Device& device, size_t size) {
|
||||||
|
|
||||||
} // Anonymous namespace
|
} // Anonymous namespace
|
||||||
|
|
||||||
BoundBuffer::BoundBuffer(MTL::Buffer* buffer_, size_t offset_, size_t size_)
|
|
||||||
: buffer{buffer_->retain()}, offset{offset_}, size{size_} {}
|
|
||||||
|
|
||||||
BoundBuffer::~BoundBuffer() {
|
|
||||||
if (buffer) {
|
|
||||||
buffer->release();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
BufferView::BufferView(MTL::Buffer* buffer_, size_t offset_, size_t size_,
|
BufferView::BufferView(MTL::Buffer* buffer_, size_t offset_, size_t size_,
|
||||||
VideoCore::Surface::PixelFormat format_)
|
VideoCore::Surface::PixelFormat format_)
|
||||||
: buffer{buffer_->retain()}, offset{offset_}, size{size_}, format{format_} {}
|
: buffer{buffer_->retain()}, offset{offset_}, size{size_}, format{format_} {}
|
||||||
|
@ -94,24 +85,29 @@ void BufferCacheRuntime::ClearBuffer(MTL::Buffer* dest_buffer, u32 offset, size_
|
||||||
void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format,
|
void BufferCacheRuntime::BindIndexBuffer(PrimitiveTopology topology, IndexFormat index_format,
|
||||||
u32 base_vertex, u32 num_indices, MTL::Buffer* buffer,
|
u32 base_vertex, u32 num_indices, MTL::Buffer* buffer,
|
||||||
u32 offset, [[maybe_unused]] u32 size) {
|
u32 offset, [[maybe_unused]] u32 size) {
|
||||||
// TODO: convert parameters to Metal enums
|
command_recorder.SetIndexBuffer(buffer, offset, index_format, topology, num_indices,
|
||||||
bound_index_buffer = {BoundBuffer(buffer, offset, size)};
|
base_vertex);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count) {
|
void BufferCacheRuntime::BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count) {
|
||||||
// TODO: bind quad index buffer
|
// TODO: bind quad index buffer
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::BindVertexBuffer(u32 index, MTL::Buffer* buffer, u32 offset, u32 size,
|
void BufferCacheRuntime::BindVertexBuffer(size_t stage, u32 index, MTL::Buffer* buffer, u32 offset,
|
||||||
u32 stride) {
|
u32 size, u32 stride) {
|
||||||
// TODO: use stride
|
// TODO: use stride
|
||||||
bound_vertex_buffers[MAX_METAL_BUFFERS - index - 1] = {BoundBuffer(buffer, offset, size)};
|
BindBuffer(stage, MAX_METAL_BUFFERS - index - 1, buffer, offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
|
void BufferCacheRuntime::BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings) {
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void BufferCacheRuntime::BindBuffer(size_t stage, u32 binding_index, MTL::Buffer* buffer,
|
||||||
|
u32 offset, u32 size) {
|
||||||
|
command_recorder.SetBuffer(stage, buffer, binding_index, offset);
|
||||||
|
}
|
||||||
|
|
||||||
void BufferCacheRuntime::ReserveNullBuffer() {
|
void BufferCacheRuntime::ReserveNullBuffer() {
|
||||||
if (!null_buffer) {
|
if (!null_buffer) {
|
||||||
null_buffer = CreateNullBuffer();
|
null_buffer = CreateNullBuffer();
|
||||||
|
|
|
@ -17,17 +17,6 @@ class CommandRecorder;
|
||||||
|
|
||||||
class BufferCacheRuntime;
|
class BufferCacheRuntime;
|
||||||
|
|
||||||
struct BoundBuffer {
|
|
||||||
BoundBuffer() = default;
|
|
||||||
BoundBuffer(MTL::Buffer* buffer_, size_t offset_, size_t size_);
|
|
||||||
|
|
||||||
~BoundBuffer();
|
|
||||||
|
|
||||||
MTL::Buffer* buffer = nil;
|
|
||||||
size_t offset{};
|
|
||||||
size_t size{};
|
|
||||||
};
|
|
||||||
|
|
||||||
struct BufferView {
|
struct BufferView {
|
||||||
BufferView(MTL::Buffer* buffer_, size_t offset_, size_t size_,
|
BufferView(MTL::Buffer* buffer_, size_t offset_, size_t size_,
|
||||||
VideoCore::Surface::PixelFormat format_ = VideoCore::Surface::PixelFormat::Invalid);
|
VideoCore::Surface::PixelFormat format_ = VideoCore::Surface::PixelFormat::Invalid);
|
||||||
|
@ -121,7 +110,8 @@ public:
|
||||||
|
|
||||||
void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count);
|
void BindQuadIndexBuffer(PrimitiveTopology topology, u32 first, u32 count);
|
||||||
|
|
||||||
void BindVertexBuffer(u32 index, MTL::Buffer* buffer, u32 offset, u32 size, u32 stride);
|
void BindVertexBuffer(size_t stage, u32 index, MTL::Buffer* buffer, u32 offset, u32 size,
|
||||||
|
u32 stride);
|
||||||
|
|
||||||
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
|
void BindVertexBuffers(VideoCommon::HostBindings<Buffer>& bindings);
|
||||||
|
|
||||||
|
@ -131,31 +121,35 @@ public:
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
void BindTransformFeedbackBuffers(VideoCommon::HostBindings<Buffer>& bindings) {}
|
void BindTransformFeedbackBuffers(VideoCommon::HostBindings<Buffer>& bindings) {}
|
||||||
|
|
||||||
std::span<u8> BindMappedUniformBuffer([[maybe_unused]] size_t stage,
|
std::span<u8> BindMappedUniformBuffer(size_t stage, u32 binding_index, u32 size) {
|
||||||
[[maybe_unused]] u32 binding_index, u32 size) {
|
|
||||||
const StagingBufferRef ref = staging_pool.Request(size, MemoryUsage::Upload);
|
const StagingBufferRef ref = staging_pool.Request(size, MemoryUsage::Upload);
|
||||||
BindBuffer(ref.buffer, static_cast<u32>(ref.offset), size);
|
BindBuffer(stage, binding_index, ref.buffer, static_cast<u32>(ref.offset), size);
|
||||||
return ref.mapped_span;
|
return ref.mapped_span;
|
||||||
}
|
}
|
||||||
|
|
||||||
void BindUniformBuffer(MTL::Buffer* buffer, u32 offset, u32 size) {
|
void BindUniformBuffer(size_t stage, u32 binding_index, MTL::Buffer* buffer, u32 offset,
|
||||||
BindBuffer(buffer, offset, size);
|
u32 size) {
|
||||||
|
BindBuffer(stage, binding_index, buffer, offset, size);
|
||||||
}
|
}
|
||||||
|
|
||||||
void BindStorageBuffer(MTL::Buffer* buffer, u32 offset, u32 size,
|
// TODO: implement
|
||||||
[[maybe_unused]] bool is_written) {
|
void BindComputeUniformBuffer(u32 binding_index, MTL::Buffer* buffer, u32 offset, u32 size) {}
|
||||||
BindBuffer(buffer, offset, size);
|
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: implement
|
||||||
|
void BindComputeStorageBuffer(u32 binding_index, Buffer& buffer, u32 offset, u32 size,
|
||||||
|
bool is_written) {}
|
||||||
|
|
||||||
// TODO: implement
|
// TODO: implement
|
||||||
void BindTextureBuffer(Buffer& buffer, u32 offset, u32 size,
|
void BindTextureBuffer(Buffer& buffer, u32 offset, u32 size,
|
||||||
VideoCore::Surface::PixelFormat format) {}
|
VideoCore::Surface::PixelFormat format) {}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void BindBuffer(MTL::Buffer* buffer, u32 offset, u32 size) {
|
void BindBuffer(size_t stage, u32 binding_index, MTL::Buffer* buffer, u32 offset, u32 size);
|
||||||
// FIXME: what should be the index?
|
|
||||||
bound_buffers[0] = BoundBuffer(buffer, offset, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void ReserveNullBuffer();
|
void ReserveNullBuffer();
|
||||||
MTL::Buffer* CreateNullBuffer();
|
MTL::Buffer* CreateNullBuffer();
|
||||||
|
@ -167,17 +161,6 @@ private:
|
||||||
// Common buffers
|
// Common buffers
|
||||||
MTL::Buffer* null_buffer = nil;
|
MTL::Buffer* null_buffer = nil;
|
||||||
MTL::Buffer* quad_index_buffer = nil;
|
MTL::Buffer* quad_index_buffer = nil;
|
||||||
|
|
||||||
// TODO: probably move this into a separate class
|
|
||||||
// Bound state
|
|
||||||
// Vertex buffers are bound to MAX_METAL_BUFFERS - index - 1, while regular buffers are bound to
|
|
||||||
// index
|
|
||||||
BoundBuffer bound_vertex_buffers[MAX_METAL_BUFFERS] = {{}};
|
|
||||||
struct {
|
|
||||||
BoundBuffer buffer;
|
|
||||||
// TODO: include index type and primitive topology
|
|
||||||
} bound_index_buffer = {};
|
|
||||||
BoundBuffer bound_buffers[MAX_METAL_BUFFERS] = {{}};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BufferCacheParams {
|
struct BufferCacheParams {
|
||||||
|
@ -189,8 +172,8 @@ struct BufferCacheParams {
|
||||||
static constexpr bool IS_OPENGL = false;
|
static constexpr bool IS_OPENGL = false;
|
||||||
static constexpr bool HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS = false;
|
static constexpr bool HAS_PERSISTENT_UNIFORM_BUFFER_BINDINGS = false;
|
||||||
static constexpr bool HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT = false;
|
static constexpr bool HAS_FULL_INDEX_AND_PRIMITIVE_SUPPORT = false;
|
||||||
static constexpr bool NEEDS_BIND_UNIFORM_INDEX = false;
|
static constexpr bool NEEDS_BIND_UNIFORM_INDEX = true;
|
||||||
static constexpr bool NEEDS_BIND_STORAGE_INDEX = false;
|
static constexpr bool NEEDS_BIND_STORAGE_INDEX = true;
|
||||||
static constexpr bool USE_MEMORY_MAPS = true;
|
static constexpr bool USE_MEMORY_MAPS = true;
|
||||||
static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = false;
|
static constexpr bool SEPARATE_IMAGE_BUFFER_BINDINGS = false;
|
||||||
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = true;
|
static constexpr bool USE_MEMORY_MAPS_FOR_UPLOADS = true;
|
||||||
|
|
|
@ -11,12 +11,86 @@ CommandRecorder::CommandRecorder(const Device& device_) : device(device_) {}
|
||||||
CommandRecorder::~CommandRecorder() = default;
|
CommandRecorder::~CommandRecorder() = default;
|
||||||
|
|
||||||
void CommandRecorder::BeginOrContinueRenderPass(MTL::RenderPassDescriptor* render_pass) {
|
void CommandRecorder::BeginOrContinueRenderPass(MTL::RenderPassDescriptor* render_pass) {
|
||||||
|
bool should_reset_bound_resources = false;
|
||||||
if (render_pass != render_state.render_pass) {
|
if (render_pass != render_state.render_pass) {
|
||||||
RequireCommandBuffer();
|
|
||||||
EndEncoding();
|
EndEncoding();
|
||||||
|
RequireCommandBuffer();
|
||||||
encoder = command_buffer->renderCommandEncoder(render_pass);
|
encoder = command_buffer->renderCommandEncoder(render_pass);
|
||||||
encoder_type = EncoderType::Render;
|
encoder_type = EncoderType::Render;
|
||||||
render_state.render_pass = render_pass;
|
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++) {
|
||||||
|
auto& bound_buffer = render_state.buffers[stage][i];
|
||||||
|
if (bound_buffer.buffer &&
|
||||||
|
(bound_buffer.needs_update || should_reset_bound_resources)) {
|
||||||
|
switch (stage) {
|
||||||
|
case 0:
|
||||||
|
GetRenderCommandEncoderUnchecked()->setVertexBuffer(bound_buffer.buffer, i,
|
||||||
|
bound_buffer.offset);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
GetRenderCommandEncoderUnchecked()->setFragmentBuffer(bound_buffer.buffer, i,
|
||||||
|
bound_buffer.offset);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bound_buffer.needs_update = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Textures
|
||||||
|
for (u8 i = 0; i < MAX_TEXTURES; i++) {
|
||||||
|
auto& bound_texture = render_state.textures[stage][i];
|
||||||
|
if (bound_texture.texture &&
|
||||||
|
(bound_texture.needs_update || should_reset_bound_resources)) {
|
||||||
|
switch (stage) {
|
||||||
|
case 0:
|
||||||
|
GetRenderCommandEncoderUnchecked()->setVertexTexture(bound_texture.texture, i);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
GetRenderCommandEncoderUnchecked()->setFragmentTexture(bound_texture.texture,
|
||||||
|
i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
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];
|
||||||
|
if (bound_sampler_state.sampler_state &&
|
||||||
|
(bound_sampler_state.needs_update || should_reset_bound_resources)) {
|
||||||
|
switch (stage) {
|
||||||
|
case 0:
|
||||||
|
GetRenderCommandEncoderUnchecked()->setVertexSamplerState(
|
||||||
|
bound_sampler_state.sampler_state, i);
|
||||||
|
break;
|
||||||
|
case 4:
|
||||||
|
GetRenderCommandEncoderUnchecked()->setFragmentSamplerState(
|
||||||
|
bound_sampler_state.sampler_state, i);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
bound_sampler_state.needs_update = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -44,7 +118,8 @@ void CommandRecorder::EndEncoding() {
|
||||||
//[encoder release];
|
//[encoder release];
|
||||||
encoder = nullptr;
|
encoder = nullptr;
|
||||||
if (encoder_type == EncoderType::Render) {
|
if (encoder_type == EncoderType::Render) {
|
||||||
render_state = {};
|
render_state.render_pass = nullptr;
|
||||||
|
render_state.pipeline_state = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@
|
||||||
#include <Metal/Metal.hpp>
|
#include <Metal/Metal.hpp>
|
||||||
#include <QuartzCore/QuartzCore.hpp>
|
#include <QuartzCore/QuartzCore.hpp>
|
||||||
|
|
||||||
|
#include "video_core/engines/maxwell_3d.h"
|
||||||
|
|
||||||
namespace Metal {
|
namespace Metal {
|
||||||
|
|
||||||
class Device;
|
class Device;
|
||||||
|
@ -16,27 +18,48 @@ constexpr size_t MAX_BUFFERS = 31;
|
||||||
constexpr size_t MAX_TEXTURES = 31;
|
constexpr size_t MAX_TEXTURES = 31;
|
||||||
constexpr size_t MAX_SAMPLERS = 31;
|
constexpr size_t MAX_SAMPLERS = 31;
|
||||||
|
|
||||||
|
struct BoundBuffer {
|
||||||
|
bool needs_update{true};
|
||||||
|
MTL::Buffer* buffer{nullptr};
|
||||||
|
size_t offset{0};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BoundTexture {
|
||||||
|
bool needs_update{true};
|
||||||
|
MTL::Texture* texture{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BoundSamplerState {
|
||||||
|
bool needs_update{true};
|
||||||
|
MTL::SamplerState* sampler_state{nullptr};
|
||||||
|
};
|
||||||
|
|
||||||
|
struct BoundIndexBuffer {
|
||||||
|
MTL::Buffer* buffer{nullptr};
|
||||||
|
size_t offset{0};
|
||||||
|
MTL::IndexType index_format;
|
||||||
|
MTL::PrimitiveType primitive_topology;
|
||||||
|
u32 num_indices;
|
||||||
|
u32 base_vertex;
|
||||||
|
};
|
||||||
|
|
||||||
struct RenderState {
|
struct RenderState {
|
||||||
MTL::RenderPassDescriptor* render_pass{nullptr};
|
MTL::RenderPassDescriptor* render_pass{nullptr};
|
||||||
MTL::RenderPipelineState* pipeline_state{nullptr};
|
MTL::RenderPipelineState* pipeline_state{nullptr};
|
||||||
|
|
||||||
MTL::Buffer* vertex_buffers[MAX_BUFFERS] = {nullptr};
|
BoundBuffer buffers[5][MAX_BUFFERS] = {{}};
|
||||||
MTL::Buffer* fragment_buffers[MAX_BUFFERS] = {nullptr};
|
BoundTexture textures[5][MAX_TEXTURES] = {{}};
|
||||||
MTL::Buffer* compute_buffers[MAX_BUFFERS] = {nullptr};
|
BoundSamplerState sampler_states[5][MAX_SAMPLERS] = {{}};
|
||||||
|
BoundIndexBuffer bound_index_buffer;
|
||||||
MTL::Texture* vertex_textures[MAX_TEXTURES] = {nullptr};
|
|
||||||
MTL::Texture* fragment_textures[MAX_TEXTURES] = {nullptr};
|
|
||||||
MTL::Texture* compute_textures[MAX_TEXTURES] = {nullptr};
|
|
||||||
|
|
||||||
MTL::SamplerState* vertex_sampler_states[MAX_SAMPLERS] = {nullptr};
|
|
||||||
MTL::SamplerState* fragment_sampler_states[MAX_SAMPLERS] = {nullptr};
|
|
||||||
MTL::SamplerState* compute_sampler_states[MAX_SAMPLERS] = {nullptr};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// TODO: whenever a render pass gets interrupted by either a compute or blit command and application
|
// 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
|
// then tries to perform a render command, begin the same render pass, but with all load actions set
|
||||||
// to "load"
|
// to "load"
|
||||||
class CommandRecorder {
|
class CommandRecorder {
|
||||||
|
using PrimitiveTopology = Tegra::Engines::Maxwell3D::Regs::PrimitiveTopology;
|
||||||
|
using IndexFormat = Tegra::Engines::Maxwell3D::Regs::IndexFormat;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
CommandRecorder(const Device& device_);
|
CommandRecorder(const Device& device_);
|
||||||
~CommandRecorder();
|
~CommandRecorder();
|
||||||
|
@ -64,10 +87,14 @@ public:
|
||||||
return command_buffer;
|
return command_buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MTL::RenderCommandEncoder* GetRenderCommandEncoderUnchecked() {
|
||||||
|
return static_cast<MTL::RenderCommandEncoder*>(encoder);
|
||||||
|
}
|
||||||
|
|
||||||
MTL::RenderCommandEncoder* GetRenderCommandEncoder() {
|
MTL::RenderCommandEncoder* GetRenderCommandEncoder() {
|
||||||
CheckIfRenderPassIsActive();
|
CheckIfRenderPassIsActive();
|
||||||
|
|
||||||
return static_cast<MTL::RenderCommandEncoder*>(encoder);
|
return GetRenderCommandEncoderUnchecked();
|
||||||
}
|
}
|
||||||
|
|
||||||
MTL::ComputeCommandEncoder* GetComputeCommandEncoder() {
|
MTL::ComputeCommandEncoder* GetComputeCommandEncoder() {
|
||||||
|
@ -90,67 +117,34 @@ public:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void SetVertexBuffer(MTL::Buffer* buffer, size_t index) {
|
inline void SetBuffer(size_t stage, MTL::Buffer* buffer, size_t index, size_t offset) {
|
||||||
if (buffer != render_state.vertex_buffers[index]) {
|
auto& bound_buffer = render_state.buffers[stage][index];
|
||||||
GetRenderCommandEncoder()->setVertexBuffer(buffer, index, 0);
|
if (buffer != bound_buffer.buffer) {
|
||||||
render_state.vertex_buffers[index] = buffer;
|
bound_buffer = {true, buffer, offset};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void SetFragmentBuffer(MTL::Buffer* buffer, size_t index) {
|
inline void SetTexture(size_t stage, MTL::Texture* texture, size_t index) {
|
||||||
if (buffer != render_state.fragment_buffers[index]) {
|
auto& bound_texture = render_state.textures[stage][index];
|
||||||
GetRenderCommandEncoder()->setFragmentBuffer(buffer, index, 0);
|
if (texture != bound_texture.texture) {
|
||||||
render_state.fragment_buffers[index] = buffer;
|
bound_texture = {true, texture};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void SetComputeBuffer(MTL::Buffer* buffer, size_t index) {
|
inline void SetSamplerState(size_t stage, MTL::SamplerState* sampler_state, size_t index) {
|
||||||
if (buffer != render_state.compute_buffers[index]) {
|
auto& bound_sampler_state = render_state.sampler_states[stage][index];
|
||||||
GetComputeCommandEncoder()->setBuffer(buffer, index, 0);
|
if (sampler_state != bound_sampler_state.sampler_state) {
|
||||||
render_state.compute_buffers[index] = buffer;
|
bound_sampler_state = {true, sampler_state};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void SetVertexTexture(MTL::Texture* texture, size_t index) {
|
inline void SetIndexBuffer(MTL::Buffer* buffer, size_t offset, IndexFormat index_format,
|
||||||
if (texture != render_state.vertex_textures[index]) {
|
PrimitiveTopology primitive_topology, u32 num_indices,
|
||||||
GetRenderCommandEncoder()->setVertexTexture(texture, index);
|
u32 base_vertex) {
|
||||||
render_state.vertex_textures[index] = texture;
|
// TODO: convert parameters to Metal enums
|
||||||
}
|
render_state.bound_index_buffer = {
|
||||||
}
|
buffer, offset, MTL::IndexTypeUInt32, MTL::PrimitiveTypeTriangle,
|
||||||
|
num_indices, base_vertex};
|
||||||
inline void SetFragmentTexture(MTL::Texture* texture, size_t index) {
|
|
||||||
if (texture != render_state.fragment_textures[index]) {
|
|
||||||
GetRenderCommandEncoder()->setFragmentTexture(texture, index);
|
|
||||||
render_state.fragment_textures[index] = texture;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void SetComputeTexture(MTL::Texture* texture, size_t index) {
|
|
||||||
if (texture != render_state.compute_textures[index]) {
|
|
||||||
GetComputeCommandEncoder()->setTexture(texture, index);
|
|
||||||
render_state.compute_textures[index] = texture;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void SetVertexSamplerState(MTL::SamplerState* sampler_state, size_t index) {
|
|
||||||
if (sampler_state != render_state.vertex_sampler_states[index]) {
|
|
||||||
GetRenderCommandEncoder()->setVertexSamplerState(sampler_state, index);
|
|
||||||
render_state.vertex_sampler_states[index] = sampler_state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void SetFragmentSamplerState(MTL::SamplerState* sampler_state, size_t index) {
|
|
||||||
if (sampler_state != render_state.fragment_sampler_states[index]) {
|
|
||||||
GetRenderCommandEncoder()->setFragmentSamplerState(sampler_state, index);
|
|
||||||
render_state.fragment_sampler_states[index] = sampler_state;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline void SetComputeSamplerState(MTL::SamplerState* sampler_state, size_t index) {
|
|
||||||
if (sampler_state != render_state.compute_sampler_states[index]) {
|
|
||||||
GetComputeCommandEncoder()->setSamplerState(sampler_state, index);
|
|
||||||
render_state.compute_sampler_states[index] = sampler_state;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -58,70 +58,146 @@ GraphicsPipeline::GraphicsPipeline(const Device& device_, CommandRecorder& comma
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsPipeline::Configure(bool is_indexed) {
|
void GraphicsPipeline::Configure(bool is_indexed) {
|
||||||
buffer_cache.UpdateGraphicsBuffers(is_indexed);
|
|
||||||
buffer_cache.BindHostGeometryBuffers(is_indexed);
|
|
||||||
|
|
||||||
texture_cache.SynchronizeGraphicsDescriptors();
|
texture_cache.SynchronizeGraphicsDescriptors();
|
||||||
|
|
||||||
// Find resources
|
|
||||||
size_t stage = 4;
|
|
||||||
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::ImageViewInOut, 32> views;
|
||||||
std::array<VideoCommon::SamplerId, 32> samplers;
|
std::array<VideoCommon::SamplerId, 32> samplers;
|
||||||
size_t view_index{};
|
size_t view_index{};
|
||||||
size_t sampler_index{};
|
size_t sampler_index{};
|
||||||
|
|
||||||
for (const auto& desc : info.texture_descriptors) {
|
// Find resources
|
||||||
for (u32 index = 0; index < desc.count; ++index) {
|
const auto& regs{maxwell3d->regs};
|
||||||
const auto handle{read_handle(desc, index)};
|
const bool via_header_index{regs.sampler_binding == Maxwell::SamplerBinding::ViaHeaderBinding};
|
||||||
views[view_index++] = {handle.first};
|
const auto configure_stage{[&](u32 stage) {
|
||||||
|
const Shader::Info& info{stage_infos[stage]};
|
||||||
VideoCommon::SamplerId sampler{texture_cache.GetGraphicsSamplerId(handle.second)};
|
buffer_cache.UnbindGraphicsStorageBuffers(stage);
|
||||||
samplers[sampler_index++] = sampler;
|
size_t ssbo_index{};
|
||||||
|
for (const auto& desc : info.storage_buffers_descriptors) {
|
||||||
|
ASSERT(desc.count == 1);
|
||||||
|
buffer_cache.BindGraphicsStorageBuffer(stage, ssbo_index, desc.cbuf_index,
|
||||||
|
desc.cbuf_offset, desc.is_written);
|
||||||
|
++ssbo_index;
|
||||||
}
|
}
|
||||||
}
|
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, via_header_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
auto a = gpu_memory->Read<u32>(addr);
|
||||||
|
// HACK: this particular texture breaks SMO
|
||||||
|
if (a == 310378931)
|
||||||
|
a = 310378932;
|
||||||
|
|
||||||
|
return TexturePair(a, via_header_index);
|
||||||
|
}};
|
||||||
|
const auto add_image{[&](const auto& desc, bool blacklist) {
|
||||||
|
for (u32 index = 0; index < desc.count; ++index) {
|
||||||
|
const auto handle{read_handle(desc, index)};
|
||||||
|
views[view_index++] = {
|
||||||
|
.index = handle.first,
|
||||||
|
.blacklist = blacklist,
|
||||||
|
.id = {},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}};
|
||||||
|
/*
|
||||||
|
if constexpr (Spec::has_texture_buffers) {
|
||||||
|
for (const auto& desc : info.texture_buffer_descriptors) {
|
||||||
|
add_image(desc, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/*
|
||||||
|
if constexpr (Spec::has_image_buffers) {
|
||||||
|
for (const auto& desc : info.image_buffer_descriptors) {
|
||||||
|
add_image(desc, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
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);
|
||||||
|
configure_stage(4);
|
||||||
|
|
||||||
|
buffer_cache.UpdateGraphicsBuffers(is_indexed);
|
||||||
|
buffer_cache.BindHostGeometryBuffers(is_indexed);
|
||||||
|
|
||||||
texture_cache.FillGraphicsImageViews<true>(std::span(views.data(), view_index));
|
texture_cache.FillGraphicsImageViews<true>(std::span(views.data(), view_index));
|
||||||
|
|
||||||
// Begin render pass
|
|
||||||
texture_cache.UpdateRenderTargets(false);
|
|
||||||
const Framebuffer* const framebuffer = texture_cache.GetFramebuffer();
|
|
||||||
if (!framebuffer) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
command_recorder.BeginOrContinueRenderPass(framebuffer->GetHandle());
|
|
||||||
|
|
||||||
command_recorder.SetRenderPipelineState(pipeline_state);
|
|
||||||
|
|
||||||
// Bind resources
|
// Bind resources
|
||||||
|
|
||||||
// HACK: try to find a texture that we can bind
|
// HACK: try to find a texture that we can bind
|
||||||
|
@ -131,8 +207,18 @@ void GraphicsPipeline::Configure(bool is_indexed) {
|
||||||
ImageView& image_view{texture_cache.GetImageView(views_it->id)};
|
ImageView& image_view{texture_cache.GetImageView(views_it->id)};
|
||||||
Sampler& sampler{texture_cache.GetSampler(*samplers_it)};
|
Sampler& sampler{texture_cache.GetSampler(*samplers_it)};
|
||||||
|
|
||||||
command_recorder.SetFragmentTexture(image_view.GetHandle(), 0);
|
command_recorder.SetTexture(4, image_view.GetHandle(), 0);
|
||||||
command_recorder.SetFragmentSamplerState(sampler.GetHandle(), 0);
|
command_recorder.SetSamplerState(4, sampler.GetHandle(), 0);
|
||||||
|
|
||||||
|
// Begin render pass
|
||||||
|
texture_cache.UpdateRenderTargets(false);
|
||||||
|
const Framebuffer* const framebuffer = texture_cache.GetFramebuffer();
|
||||||
|
if (!framebuffer) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
command_recorder.BeginOrContinueRenderPass(framebuffer->GetHandle());
|
||||||
|
|
||||||
|
command_recorder.SetRenderPipelineState(pipeline_state);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GraphicsPipeline::MakePipeline(MTL::RenderPassDescriptor* render_pass) {
|
void GraphicsPipeline::MakePipeline(MTL::RenderPassDescriptor* render_pass) {
|
||||||
|
|
|
@ -272,7 +272,7 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||||
std::cout << code << std::endl;
|
std::cout << code << std::endl;
|
||||||
MTL::CompileOptions* compile_options = MTL::CompileOptions::alloc()->init();
|
MTL::CompileOptions* compile_options = MTL::CompileOptions::alloc()->init();
|
||||||
NS::Error* error = nullptr;
|
NS::Error* error = nullptr;
|
||||||
MTL::Library* library = device.GetDevice()->newLibrary(
|
[[maybe_unused]] MTL::Library* library = device.GetDevice()->newLibrary(
|
||||||
NS::String::string(code.c_str(), NS::ASCIIStringEncoding), compile_options, &error);
|
NS::String::string(code.c_str(), NS::ASCIIStringEncoding), compile_options, &error);
|
||||||
if (error) {
|
if (error) {
|
||||||
LOG_ERROR(Render_Metal, "failed to create library: {}",
|
LOG_ERROR(Render_Metal, "failed to create library: {}",
|
||||||
|
@ -280,11 +280,11 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||||
// HACK
|
// HACK
|
||||||
std::cout << error->description()->cString(NS::ASCIIStringEncoding) << std::endl;
|
std::cout << error->description()->cString(NS::ASCIIStringEncoding) << std::endl;
|
||||||
// HACK
|
// HACK
|
||||||
throw;
|
// throw;
|
||||||
}
|
}
|
||||||
|
|
||||||
functions[index - 1] =
|
// functions[stage_index] =
|
||||||
library->newFunction(NS::String::string("main_", NS::ASCIIStringEncoding));
|
// library->newFunction(NS::String::string("main_", NS::ASCIIStringEncoding));
|
||||||
previous_stage = &program;
|
previous_stage = &program;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -316,8 +316,8 @@ std::unique_ptr<GraphicsPipeline> PipelineCache::CreateGraphicsPipeline(
|
||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
fragment float4 fragmentMain(VertexOut in [[stage_in]], texture2d<float> tex [[texture(0)]], sampler samplr [[sampler(0)]]) {
|
fragment float4 fragmentMain(VertexOut in [[stage_in]], texture2d<float> tex [[texture(0)]],
|
||||||
return tex.sample(samplr, in.texCoord);
|
sampler samplr [[sampler(0)]]) { return tex.sample(samplr, in.texCoord);
|
||||||
}
|
}
|
||||||
)",
|
)",
|
||||||
NS::ASCIIStringEncoding),
|
NS::ASCIIStringEncoding),
|
||||||
|
|
|
@ -42,18 +42,20 @@ void RendererMetal::Composite(std::span<const Tegra::FramebufferConfig> framebuf
|
||||||
render_pass_descriptor->colorAttachments()->object(0)->setTexture(
|
render_pass_descriptor->colorAttachments()->object(0)->setTexture(
|
||||||
swap_chain.GetDrawableTexture());
|
swap_chain.GetDrawableTexture());
|
||||||
|
|
||||||
command_recorder.BeginOrContinueRenderPass(render_pass_descriptor);
|
// Bind the source texture
|
||||||
|
|
||||||
// Blit the framebuffer to the drawable texture
|
|
||||||
// TODO: acquire the texture from @ref framebuffers
|
// TODO: acquire the texture from @ref framebuffers
|
||||||
const Framebuffer* const framebuffer = rasterizer.texture_cache.GetFramebuffer();
|
const Framebuffer* const framebuffer = rasterizer.texture_cache.GetFramebuffer();
|
||||||
if (!framebuffer) {
|
if (!framebuffer) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
MTL::Texture* src_texture = framebuffer->GetHandle()->colorAttachments()->object(0)->texture();
|
MTL::Texture* src_texture = framebuffer->GetHandle()->colorAttachments()->object(0)->texture();
|
||||||
|
command_recorder.SetTexture(4, src_texture, 0);
|
||||||
|
command_recorder.SetSamplerState(4, blit_sampler_state, 0);
|
||||||
|
|
||||||
|
command_recorder.BeginOrContinueRenderPass(render_pass_descriptor);
|
||||||
|
|
||||||
|
// Blit the framebuffer to the drawable texture
|
||||||
command_recorder.SetRenderPipelineState(blit_pipeline_state);
|
command_recorder.SetRenderPipelineState(blit_pipeline_state);
|
||||||
command_recorder.SetFragmentTexture(src_texture, 0);
|
|
||||||
command_recorder.SetFragmentSamplerState(blit_sampler_state, 0);
|
|
||||||
|
|
||||||
// Draw a full screen triangle which will get clipped to a rectangle
|
// Draw a full screen triangle which will get clipped to a rectangle
|
||||||
command_recorder.GetRenderCommandEncoder()->drawPrimitives(MTL::PrimitiveTypeTriangle,
|
command_recorder.GetRenderCommandEncoder()->drawPrimitives(MTL::PrimitiveTypeTriangle,
|
||||||
|
|
Loading…
Reference in a new issue