suyu/src/video_core/renderer_metal/mtl_graphics_pipeline.cpp

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