miroir de https://git.suyu.dev/suyu/suyu.git
170 lignes
6.6 KiB
C++
170 lignes
6.6 KiB
C++
// SPDX-FileCopyrightText: Copyright 2024 suyu Emulator Project
|
|
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
|
|
#include <algorithm>
|
|
#include <iostream>
|
|
#include <span>
|
|
|
|
#include <boost/container/small_vector.hpp>
|
|
#include <boost/container/static_vector.hpp>
|
|
|
|
#include "video_core/renderer_metal/mtl_graphics_pipeline.h"
|
|
|
|
#include "common/bit_field.h"
|
|
#include "video_core/buffer_cache/buffer_cache_base.h"
|
|
#include "video_core/engines/maxwell_3d.h"
|
|
#include "video_core/renderer_metal/mtl_command_recorder.h"
|
|
#include "video_core/renderer_metal/mtl_device.h"
|
|
#include "video_core/shader_notify.h"
|
|
#include "video_core/texture_cache/texture_cache.h"
|
|
#include "video_core/texture_cache/texture_cache_base.h"
|
|
|
|
namespace Metal {
|
|
namespace {
|
|
using Tegra::Texture::TexturePair;
|
|
using VideoCommon::NUM_RT;
|
|
using VideoCore::Surface::PixelFormat;
|
|
using VideoCore::Surface::PixelFormatFromDepthFormat;
|
|
using VideoCore::Surface::PixelFormatFromRenderTargetFormat;
|
|
using Maxwell = Tegra::Engines::Maxwell3D::Regs;
|
|
} // Anonymous namespace
|
|
|
|
GraphicsPipeline::GraphicsPipeline(const Device& device_, CommandRecorder& command_recorder_,
|
|
const GraphicsPipelineCacheKey& key_, BufferCache& buffer_cache_,
|
|
TextureCache& texture_cache_,
|
|
VideoCore::ShaderNotify* shader_notify,
|
|
std::array<MTL::Function*, NUM_STAGES> functions_,
|
|
const std::array<const Shader::Info*, NUM_STAGES>& infos)
|
|
: device{device_}, command_recorder{command_recorder_}, key{key_}, buffer_cache{buffer_cache_},
|
|
texture_cache{texture_cache_}, functions{functions_} {
|
|
if (shader_notify) {
|
|
shader_notify->MarkShaderBuilding();
|
|
}
|
|
for (size_t stage = 0; stage < NUM_STAGES; ++stage) {
|
|
const Shader::Info* const info{infos[stage]};
|
|
if (!info) {
|
|
continue;
|
|
}
|
|
stage_infos[stage] = *info;
|
|
}
|
|
Validate();
|
|
// TODO: is the framebuffer available by this time?
|
|
Framebuffer* framebuffer = texture_cache.GetFramebuffer();
|
|
if (!framebuffer) {
|
|
LOG_DEBUG(Render_Metal, "framebuffer not available");
|
|
return;
|
|
}
|
|
MakePipeline(framebuffer->GetHandle());
|
|
}
|
|
|
|
void GraphicsPipeline::Configure(bool is_indexed) {
|
|
buffer_cache.UpdateGraphicsBuffers(is_indexed);
|
|
buffer_cache.BindHostGeometryBuffers(is_indexed);
|
|
|
|
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: hardcode the image
|
|
if (a != 310378932)
|
|
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;
|
|
}
|
|
}
|
|
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
|
|
|
|
// HACK: try to find a texture that we can bind
|
|
const VideoCommon::ImageViewInOut* views_it{views.data()};
|
|
const VideoCommon::SamplerId* samplers_it{samplers.data()};
|
|
|
|
ImageView& image_view{texture_cache.GetImageView(views_it->id)};
|
|
Sampler& sampler{texture_cache.GetSampler(*samplers_it)};
|
|
|
|
command_recorder.SetFragmentTexture(image_view.GetHandle(), 0);
|
|
command_recorder.SetFragmentSamplerState(sampler.GetHandle(), 0);
|
|
}
|
|
|
|
void GraphicsPipeline::MakePipeline(MTL::RenderPassDescriptor* render_pass) {
|
|
MTL::RenderPipelineDescriptor* pipeline_descriptor =
|
|
MTL::RenderPipelineDescriptor::alloc()->init();
|
|
pipeline_descriptor->setVertexFunction(functions[0]);
|
|
pipeline_descriptor->setFragmentFunction(functions[1]);
|
|
// pipeline_descriptor->setVertexDescriptor(vertex_descriptor);
|
|
// TODO: get the attachment count from render pass descriptor
|
|
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?
|
|
if (!render_pass_attachment->texture()) {
|
|
continue;
|
|
}
|
|
|
|
auto* color_attachment = pipeline_descriptor->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);
|
|
if (error) {
|
|
LOG_ERROR(Render_Metal, "failed to create pipeline state: {}",
|
|
error->description()->cString(NS::ASCIIStringEncoding));
|
|
}
|
|
}
|
|
|
|
void GraphicsPipeline::Validate() {
|
|
// TODO: validate pipeline
|
|
}
|
|
|
|
} // namespace Metal
|