Skip to content

Commit

Permalink
Framebuffer Effects 1.0 (Kenix3#6)
Browse files Browse the repository at this point in the history
* Add initial copy framebuffer and draw framebuffer commands

* Implement DirectX framebuffer copy

* Implement OpenGL framebuffer copy

* Implement Metal framebuffer copy

* tweaks for metal viewport and int conversion

* resolve opengl msaa

* remove unused code paths
  • Loading branch information
Archez authored Feb 25, 2024
1 parent 46ec75c commit ace86f4
Show file tree
Hide file tree
Showing 8 changed files with 406 additions and 42 deletions.
34 changes: 25 additions & 9 deletions include/libultraship/libultra/gbi.h
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@
// RDP Cmd
#define G_SETGRAYSCALE 0x39
#define G_EXTRAGEOMETRYMODE 0x3a
#define G_COPYFB 0x3b
#define G_IMAGERECT 0x3c
#define G_SETINTENSITY 0x40

/*
Expand Down Expand Up @@ -2643,22 +2645,36 @@ typedef union {
#define gsSPInvalidateTexCache() \
{ _SHIFTL(G_INVALTEXCACHE, 24, 8), 0 }

#define gsSPSetFB(pkt, fb) \
{ \
Gfx* _g = (Gfx*)(pkt); \
\
_g->words.w0 = _SHIFTL(G_SETFB, 24, 8); \
_g->words.w1 = fb; \
}
#define gSPSetFB(pkt, f, s, w, i) gSetImage(pkt, G_SETFB, f, s, w, i)

#define gsSPResetFB(pkt) \
#define gSPResetFB(pkt) \
{ \
Gfx* _g = (Gfx*)(pkt); \
\
_g->words.w0 = _SHIFTL(G_RESETFB, 24, 8); \
_g->words.w1 = 0; \
}

#define gDPCopyFB(pkt, dst, src, once, copiedPtr) \
{ \
Gfx* _g = (Gfx*)(pkt); \
\
_g->words.w0 = _SHIFTL(G_COPYFB, 24, 8) | _SHIFTL(dst, 11, 11) | _SHIFTL(src, 0, 11) | _SHIFTL(once, 22, 1); \
_g->words.w1 = (uintptr_t)copiedPtr; \
}

#define gDPImageRectangle(pkt, x0, y0, s0, t0, x1, y1, s1, t1, tile, iw, ih) \
{ \
Gfx *_g0 = (Gfx*)(pkt), *_g1 = (Gfx*)(pkt), *_g2 = (Gfx*)(pkt); \
\
_g0->words.w0 = _SHIFTL(G_IMAGERECT, 24, 8) | _SHIFTL((tile), 0, 3); \
_g0->words.w1 = _SHIFTL((iw), 16, 16) | _SHIFTL((ih), 0, 16); \
_g1->words.w0 = _SHIFTL((x0), 16, 16) | _SHIFTL((y0), 0, 16); \
_g1->words.w1 = _SHIFTL((s0), 16, 16) | _SHIFTL((t0), 0, 16); \
_g2->words.w0 = _SHIFTL((x1), 16, 16) | _SHIFTL((y1), 0, 16); \
_g2->words.w1 = _SHIFTL((s1), 16, 16) | _SHIFTL((t1), 0, 16); \
}

#define gSPGrayscale(pkt, state) \
{ \
Gfx* _g = (Gfx*)(pkt); \
Expand Down Expand Up @@ -4075,4 +4091,4 @@ typedef union {

#endif

#endif
#endif
73 changes: 73 additions & 0 deletions src/graphic/Fast3D/gfx_direct3d11.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -966,6 +966,78 @@ void gfx_d3d11_select_texture_fb(int fbID) {
gfx_d3d11_select_texture(tile, d3d.framebuffers[fbID].texture_id);
}

void gfx_d3d11_copy_framebuffer(int fb_dst_id, int fb_src_id) {
if (fb_src_id >= (int)d3d.framebuffers.size() || fb_dst_id >= (int)d3d.framebuffers.size()) {
return;
}

Framebuffer& fb_dst = d3d.framebuffers[fb_dst_id];
Framebuffer& fb_src = d3d.framebuffers[fb_src_id];

TextureData& td_dst = d3d.textures[fb_dst.texture_id];
TextureData& td_src = d3d.textures[fb_src.texture_id];

// Skip copying framebuffers that don't have the same width
if (td_src.width != td_dst.width) {
return;
}

// Textures are the same size so we can do a direct copy or resolve
if (td_src.height == td_dst.height) {
if (fb_src.msaa_level <= 1) {
d3d.context->CopyResource(td_dst.texture.Get(), td_src.texture.Get());
} else {
d3d.context->ResolveSubresource(td_dst.texture.Get(), 0, td_src.texture.Get(), 0,
DXGI_FORMAT_R8G8B8A8_UNORM);
}
return;
}

D3D11_BOX region;
region.left = 0;
region.right = td_src.width;
region.top = 0;
region.bottom = td_src.height;
region.front = 0;
region.back = 1;

// Account for source framebuffer having the menu bar open
if (td_src.height > td_dst.height) {
region.top = td_src.height - td_dst.height;
}

// We can't region copy a multi-sample texture to a single sample texture
if (fb_src.msaa_level <= 1) {
d3d.context->CopySubresourceRegion(td_dst.texture.Get(), 0, 0, 0, 0, td_src.texture.Get(), 0, &region);
} else {
// Setup a temporary texture
TextureData td_resolved;
td_resolved.width = td_src.width;
td_resolved.height = td_src.height;

D3D11_TEXTURE2D_DESC texture_desc;
texture_desc.Width = td_src.width;
texture_desc.Height = td_src.height;
texture_desc.Usage = D3D11_USAGE_DEFAULT;
texture_desc.BindFlags = D3D11_BIND_SHADER_RESOURCE;
texture_desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
texture_desc.CPUAccessFlags = 0;
texture_desc.MiscFlags = 0;
texture_desc.ArraySize = 1;
texture_desc.MipLevels = 1;
texture_desc.SampleDesc.Count = 1;
texture_desc.SampleDesc.Quality = 0;

ThrowIfFailed(d3d.device->CreateTexture2D(&texture_desc, nullptr, td_resolved.texture.GetAddressOf()));

// Resolve multi-sample to temporary
d3d.context->ResolveSubresource(td_resolved.texture.Get(), 0, td_src.texture.Get(), 0,
DXGI_FORMAT_R8G8B8A8_UNORM);
// Then copy the region to the destination
d3d.context->CopySubresourceRegion(td_dst.texture.Get(), 0, 0, 0, 0, td_resolved.texture.Get(), 0, &region);
}
}

void gfx_d3d11_set_texture_filter(FilteringMode mode) {
d3d.current_filter_mode = mode;
gfx_texture_cache_clear();
Expand Down Expand Up @@ -1118,6 +1190,7 @@ struct GfxRenderingAPI gfx_direct3d11_api = { gfx_d3d11_get_name,
gfx_d3d11_create_framebuffer,
gfx_d3d11_update_framebuffer_parameters,
gfx_d3d11_start_draw_to_framebuffer,
gfx_d3d11_copy_framebuffer,
gfx_d3d11_clear_framebuffer,
gfx_d3d11_resolve_msaa_color_buffer,
gfx_d3d11_get_pixel_depth,
Expand Down
5 changes: 5 additions & 0 deletions src/graphic/Fast3D/gfx_gx2.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -752,6 +752,10 @@ void gfx_gx2_select_texture_fb(int fb) {
GX2SetPixelSampler(&buffer->sampler, location);
}

void gfx_gx2_copy_framebuffer(int fb_dst_id, int fb_src_id, int left, int top, bool flip_y, bool use_back) {
// TODO: Implement framebuffer texture copy
}

static std::unordered_map<std::pair<float, float>, uint16_t, hash_pair_ff>
gfx_gx2_get_pixel_depth(int fb_id, const std::set<std::pair<float, float>>& coordinates) {
struct Framebuffer* buffer = (struct Framebuffer*)fb_id;
Expand Down Expand Up @@ -855,6 +859,7 @@ struct GfxRenderingAPI gfx_gx2_api = { gfx_gx2_get_name,
gfx_gx2_create_framebuffer,
gfx_gx2_update_framebuffer_parameters,
gfx_gx2_start_draw_to_framebuffer,
gfx_gx2_copy_framebuffer,
gfx_gx2_clear_framebuffer,
gfx_gx2_resolve_msaa_color_buffer,
gfx_gx2_get_pixel_depth,
Expand Down
112 changes: 89 additions & 23 deletions src/graphic/Fast3D/gfx_metal.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ struct FramebufferMetal {
struct ShaderProgramMetal* last_shader_program;
MTL::Texture* last_bound_textures[SHADER_MAX_TEXTURES];
MTL::SamplerState* last_bound_samplers[SHADER_MAX_TEXTURES];
MTL::ScissorRect scissor_rect;
MTL::Viewport viewport;

int8_t last_depth_test = -1;
int8_t last_depth_mask = -1;
Expand Down Expand Up @@ -476,30 +478,29 @@ static void gfx_metal_set_zmode_decal(bool zmode_decal) {
}

static void gfx_metal_set_viewport(int x, int y, int width, int height) {
MTL::Viewport viewport;
viewport.originX = x;
viewport.originY = mctx.render_target_height - y - height;
viewport.width = width;
viewport.height = height;
viewport.znear = 0;
viewport.zfar = 1;

auto current_framebuffer = mctx.framebuffers[mctx.current_framebuffer];
current_framebuffer.command_encoder->setViewport(viewport);
FramebufferMetal& fb = mctx.framebuffers[mctx.current_framebuffer];

fb.viewport.originX = x;
fb.viewport.originY = mctx.render_target_height - y - height;
fb.viewport.width = width;
fb.viewport.height = height;
fb.viewport.znear = 0;
fb.viewport.zfar = 1;

fb.command_encoder->setViewport(fb.viewport);
}

static void gfx_metal_set_scissor(int x, int y, int width, int height) {
FramebufferMetal fb = mctx.framebuffers[mctx.current_framebuffer];
FramebufferMetal& fb = mctx.framebuffers[mctx.current_framebuffer];
TextureDataMetal tex = mctx.textures[fb.texture_id];

MTL::ScissorRect rect;
// clamp to viewport size as metal does not support larger values than viewport size
rect.x = std::max(0, std::min<int>(x, tex.width));
rect.y = std::max(0, std::min<int>(mctx.render_target_height - y - height, tex.height));
rect.width = std::max(0, std::min<int>(x + width, tex.width));
rect.height = std::max(0, std::min<int>(height, tex.height));
fb.scissor_rect.x = std::max(0, std::min<int>(x, tex.width));
fb.scissor_rect.y = std::max(0, std::min<int>(mctx.render_target_height - y - height, tex.height));
fb.scissor_rect.width = std::max(0, std::min<int>(x + width, tex.width));
fb.scissor_rect.height = std::max(0, std::min<int>(height, tex.height));

fb.command_encoder->setScissorRect(rect);
fb.command_encoder->setScissorRect(fb.scissor_rect);
}

static void gfx_metal_set_use_alpha(bool use_alpha) {
Expand Down Expand Up @@ -658,6 +659,8 @@ void gfx_metal_end_frame(void) {
fb.last_bound_textures[i] = nullptr;
fb.last_bound_samplers[i] = nullptr;
}
memset(&fb.viewport, 0, sizeof(MTL::Viewport));
memset(&fb.scissor_rect, 0, sizeof(MTL::ScissorRect));
fb.last_depth_test = -1;
fb.last_depth_mask = -1;
fb.last_zmode_decal = -1;
Expand Down Expand Up @@ -706,8 +709,7 @@ static void gfx_metal_setup_screen_framebuffer(uint32_t width, uint32_t height)
MTL::RenderPassDescriptor* render_pass_descriptor = MTL::RenderPassDescriptor::renderPassDescriptor();
MTL::ClearColor clear_color = MTL::ClearColor::Make(0, 0, 0, 1);
render_pass_descriptor->colorAttachments()->object(0)->setTexture(tex.texture);
render_pass_descriptor->colorAttachments()->object(0)->setLoadAction(msaa_enabled ? MTL::LoadActionLoad
: MTL::LoadActionClear);
render_pass_descriptor->colorAttachments()->object(0)->setLoadAction(MTL::LoadActionLoad);
render_pass_descriptor->colorAttachments()->object(0)->setStoreAction(MTL::StoreActionStore);
render_pass_descriptor->colorAttachments()->object(0)->setClearColor(clear_color);

Expand Down Expand Up @@ -813,14 +815,13 @@ static void gfx_metal_update_framebuffer_parameters(int fb_id, uint32_t width, u
if (fb_msaa_enabled) {
render_pass_descriptor->colorAttachments()->object(0)->setTexture(tex.msaaTexture);
render_pass_descriptor->colorAttachments()->object(0)->setResolveTexture(tex.texture);
render_pass_descriptor->colorAttachments()->object(0)->setLoadAction(MTL::LoadActionClear);
render_pass_descriptor->colorAttachments()->object(0)->setLoadAction(MTL::LoadActionLoad);
render_pass_descriptor->colorAttachments()->object(0)->setStoreAction(
MTL::StoreActionMultisampleResolve);
MTL::StoreActionStoreAndMultisampleResolve);
render_pass_descriptor->colorAttachments()->object(0)->setClearColor(clear_color);
} else {
render_pass_descriptor->colorAttachments()->object(0)->setTexture(tex.texture);
render_pass_descriptor->colorAttachments()->object(0)->setLoadAction(
fb_id == 0 && game_msaa_enabled ? MTL::LoadActionLoad : MTL::LoadActionClear);
render_pass_descriptor->colorAttachments()->object(0)->setLoadAction(MTL::LoadActionLoad);
render_pass_descriptor->colorAttachments()->object(0)->setStoreAction(MTL::StoreActionStore);
render_pass_descriptor->colorAttachments()->object(0)->setClearColor(clear_color);
}
Expand Down Expand Up @@ -1017,6 +1018,70 @@ void gfx_metal_select_texture_fb(int fb_id) {
gfx_metal_select_texture(tile, mctx.framebuffers[fb_id].texture_id);
}

void gfx_metal_copy_framebuffer(int fb_dst_id, int fb_src_id) {
if (fb_src_id >= (int)mctx.framebuffers.size() || fb_dst_id >= (int)mctx.framebuffers.size()) {
return;
}

FramebufferMetal& source_framebuffer = mctx.framebuffers[fb_src_id];

int source_texture_id = source_framebuffer.texture_id;
MTL::Texture* source_texture = mctx.textures[source_texture_id].texture;

int target_texture_id = mctx.framebuffers[fb_dst_id].texture_id;
MTL::Texture* target_texture = mctx.textures[target_texture_id].texture;

// Skip copying framebuffers that don't have the same width
if (source_texture->width() != target_texture->width()) {
return;
}

// End the current render encoder
source_framebuffer.command_encoder->endEncoding();

// Create a blit encoder
MTL::BlitCommandEncoder* blit_encoder = source_framebuffer.command_buffer->blitCommandEncoder();
blit_encoder->setLabel(NS::String::string("Copy Framebuffer Encoder", NS::UTF8StringEncoding));

MTL::Origin source_origin = MTL::Origin(0, 0, 0);
MTL::Origin target_origin = MTL::Origin(0, 0, 0);
MTL::Size source_size = MTL::Size(source_texture->width(), source_texture->height(), 1);

// Account for source framebuffer having the menu bar open
if (source_texture->height() > target_texture->height()) {
auto diff = source_texture->height() - target_texture->height();
source_size.height -= diff;
source_origin.y = diff;
}

// Copy the texture over using the origins and size
blit_encoder->copyFromTexture(source_texture, 0, 0, source_origin, source_size, target_texture, 0, 0,
target_origin);
blit_encoder->endEncoding();

// Create a new render encoder back onto the framebuffer
source_framebuffer.command_encoder =
source_framebuffer.command_buffer->renderCommandEncoder(source_framebuffer.render_pass_descriptor);

std::string fbce_label = fmt::format("FrameBuffer {} Command Encoder After Copy", fb_src_id);
source_framebuffer.command_encoder->setLabel(NS::String::string(fbce_label.c_str(), NS::UTF8StringEncoding));
source_framebuffer.command_encoder->setDepthClipMode(MTL::DepthClipModeClamp);
source_framebuffer.command_encoder->setViewport(source_framebuffer.viewport);
source_framebuffer.command_encoder->setScissorRect(source_framebuffer.scissor_rect);

// Reset the framebuffer so the encoder is setup again when rendering triangles
source_framebuffer.has_bounded_vertex_buffer = false;
source_framebuffer.has_bounded_fragment_buffer = false;
source_framebuffer.last_shader_program = nullptr;
for (int i = 0; i < SHADER_MAX_TEXTURES; i++) {
source_framebuffer.last_bound_textures[i] = nullptr;
source_framebuffer.last_bound_samplers[i] = nullptr;
}
source_framebuffer.last_depth_test = -1;
source_framebuffer.last_depth_mask = -1;
source_framebuffer.last_zmode_decal = -1;
}

void gfx_metal_set_texture_filter(FilteringMode mode) {
mctx.current_filter_mode = mode;
gfx_texture_cache_clear();
Expand Down Expand Up @@ -1056,6 +1121,7 @@ struct GfxRenderingAPI gfx_metal_api = { gfx_metal_get_name,
gfx_metal_create_framebuffer,
gfx_metal_update_framebuffer_parameters,
gfx_metal_start_draw_to_framebuffer,
gfx_metal_copy_framebuffer,
gfx_metal_clear_framebuffer,
gfx_metal_resolve_msaa_color_buffer,
gfx_metal_get_pixel_depth,
Expand Down
Loading

0 comments on commit ace86f4

Please sign in to comment.