mirror of
https://github.com/libretro/parallel-gx1.git
synced 2026-11-07 04:26:01 -10:00
D3D11: the XFB texture handed to the frontend in place
With version 2 of the frontend interface, a picture whose draw would be a plain copy is not drawn: the XFB texture itself is named with set_texture and the frontend reads it where it is. That is the case when the XFB fills the whole output from its top-left corner at the same size, with one layer, no stereo, no colour correction and the default resampling; with crop overscan on, a full-height XFB at the internal resolution. Anything else is drawn into the output's targets as before. The immediate context orders the frontend's reads before anything later drawn into the texture, so no sync index is waited on. Fields that repeat the picture keep its XFB copy locked as the one being shown, and fields that blank the screen lock it until another picture replaces it, so the copy the frontend shows again is never written meanwhile. The hook is FrameOutput::PresentTexture, reached through AbstractGfx::PresentTexture: an output that only hands on its own targets returns false and the picture is drawn.
This commit is contained in:
1 parent
1329ca90c6
commit
14e21584dd
8 files changed
+121
No files matched your search
@@ -118,6 +118,24 @@ void DX11Output::Present()
|
||||
}
|
||||
}
|
||||
|
||||
bool DX11Output::PresentTexture(const AbstractTexture* texture, u32 width, u32 height)
|
||||
{
|
||||
unsigned depth;
|
||||
|
||||
/* version 2 only: version 1 reads pixel shader slot 0, which needs a
|
||||
* view of one 2D texture. The immediate context orders the frontend's
|
||||
* reads before anything drawn into the texture after; no sync index
|
||||
* is waited on, as none of the output's targets is drawn into. */
|
||||
if (!m_v2)
|
||||
return false;
|
||||
m_v2->set_texture(m_v2->handle,
|
||||
static_cast<const DX11::DXTexture*>(texture)->GetD3DTexture());
|
||||
depth = D3D11Suspend();
|
||||
video_cb(RETRO_HW_FRAME_BUFFER_VALID, width, height, 0);
|
||||
D3D11Resume(depth);
|
||||
return true;
|
||||
}
|
||||
|
||||
DX12Output::DX12Output(retro_hw_render_interface_d3d12* iface, u32 width, u32 height)
|
||||
: m_iface(iface), m_width(width), m_height(height), m_current(0),
|
||||
m_v2(iface->interface_version >= RETRO_HW_RENDER_INTERFACE_D3D12_VERSION_2)
|
||||
|
||||
@@ -30,6 +30,7 @@ public:
|
||||
|
||||
AbstractFramebuffer* Acquire() override;
|
||||
void Present() override;
|
||||
bool PresentTexture(const AbstractTexture* texture, u32 width, u32 height) override;
|
||||
u32 GetWidth() const override { return m_width; }
|
||||
u32 GetHeight() const override { return m_height; }
|
||||
AbstractTextureFormat GetFormat() const override { return AbstractTextureFormat::RGBA8; }
|
||||
|
||||
@@ -179,6 +179,11 @@ void Gfx::PresentBackbuffer()
|
||||
m_output->Present();
|
||||
}
|
||||
|
||||
bool Gfx::PresentTexture(const AbstractTexture* texture, u32 width, u32 height)
|
||||
{
|
||||
return m_output->PresentTexture(texture, width, height);
|
||||
}
|
||||
|
||||
void Gfx::SetFramebuffer(AbstractFramebuffer* framebuffer)
|
||||
{
|
||||
if (m_current_framebuffer == framebuffer)
|
||||
|
||||
@@ -64,6 +64,7 @@ public:
|
||||
u32 groupsize_z, u32 groups_x, u32 groups_y, u32 groups_z) override;
|
||||
bool BindBackbuffer(const ClearColor& clear_color = {}) override;
|
||||
void PresentBackbuffer() override;
|
||||
bool PresentTexture(const AbstractTexture* texture, u32 width, u32 height) override;
|
||||
|
||||
void Flush() override;
|
||||
void WaitForGPUIdle() override;
|
||||
|
||||
@@ -109,6 +109,14 @@ public:
|
||||
// Presents the backbuffer to the window system, or "swaps buffers".
|
||||
virtual void PresentBackbuffer() {}
|
||||
|
||||
/* Presents the top-left width x height of a texture as the picture, in
|
||||
* place of BindBackbuffer, a draw and PresentBackbuffer; false when the
|
||||
* backend cannot, and nothing was done. */
|
||||
virtual bool PresentTexture(const AbstractTexture* texture, u32 width, u32 height)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Shader modules/objects.
|
||||
virtual std::unique_ptr<AbstractShader>
|
||||
CreateShaderFromSource(ShaderStage stage, std::string_view source,
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
#include "VideoCommon/TextureConfig.h"
|
||||
|
||||
class AbstractFramebuffer;
|
||||
class AbstractTexture;
|
||||
|
||||
/* Where a backend draws the picture it presents, and what hands that
|
||||
* picture to whoever shows it: the frontend owns the window, so this is
|
||||
@@ -23,6 +24,16 @@ public:
|
||||
/* The picture is complete: hands it on. */
|
||||
virtual void Present() = 0;
|
||||
|
||||
/* Hands on the top-left width x height of a texture as the picture,
|
||||
* where it is, in place of one drawn into what Acquire returns; false
|
||||
* when this output only hands on its own targets. The caller keeps the
|
||||
* texture and its contents as they are for as long as it is the last
|
||||
* picture handed on. */
|
||||
virtual bool PresentTexture(const AbstractTexture* texture, u32 width, u32 height)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
virtual u32 GetWidth() const = 0;
|
||||
virtual u32 GetHeight() const = 0;
|
||||
virtual AbstractTextureFormat GetFormat() const = 0;
|
||||
|
||||
@@ -136,6 +136,11 @@ bool Presenter::FetchXFB(u32 xfb_addr, u32 fb_width, u32 fb_stride, u32 fb_heigh
|
||||
{
|
||||
// Game is blanking the screen
|
||||
m_xfb_entry.reset();
|
||||
if (m_handed_entry && !m_handed_locked)
|
||||
{
|
||||
m_handed_entry->AcquireContentLock();
|
||||
m_handed_locked = true;
|
||||
}
|
||||
m_xfb_rect = MathUtil::Rectangle<int>();
|
||||
m_last_xfb_id = std::numeric_limits<u64>::max();
|
||||
}
|
||||
@@ -889,6 +894,13 @@ void Presenter::Present()
|
||||
|
||||
UpdateDrawRectangle();
|
||||
|
||||
if (PresentXFBInPlace())
|
||||
{
|
||||
const MathUtil::Rectangle<int> rect = AdjustForCustomCrop(m_xfb_rect);
|
||||
SetSuggestedWindowSize(rect.GetWidth(), rect.GetHeight());
|
||||
return;
|
||||
}
|
||||
|
||||
g_gfx->BeginUtilityDrawing();
|
||||
const bool backbuffer_bound = g_gfx->BindBackbuffer({{0.0f, 0.0f, 0.0f, 1.0f}});
|
||||
|
||||
@@ -906,6 +918,9 @@ void Presenter::Present()
|
||||
// Present to the window system.
|
||||
g_gfx->PresentBackbuffer();
|
||||
|
||||
/* what the frontend shows now was drawn */
|
||||
ReleaseHandedEntry();
|
||||
|
||||
if (m_xfb_entry)
|
||||
{
|
||||
// Update the window size based on the frame that was just rendered.
|
||||
@@ -917,6 +932,54 @@ void Presenter::Present()
|
||||
g_gfx->EndUtilityDrawing();
|
||||
}
|
||||
|
||||
bool Presenter::PresentXFBInPlace()
|
||||
{
|
||||
const AbstractTexture* texture = m_xfb_entry->texture.get();
|
||||
const TextureConfig& config = texture->GetConfig();
|
||||
MathUtil::Rectangle<int> target_rc = GetTargetRectangle();
|
||||
MathUtil::Rectangle<int> source_rc = AdjustForCustomCrop(m_xfb_rect);
|
||||
int width;
|
||||
int height;
|
||||
|
||||
AdjustRectanglesToFitBounds(&target_rc, &source_rc, m_backbuffer_width, m_backbuffer_height);
|
||||
width = source_rc.GetWidth();
|
||||
height = source_rc.GetHeight();
|
||||
|
||||
/* what the draw would do is a copy: one layer, no correction or
|
||||
* resampling, the whole backbuffer from the texture's top-left corner
|
||||
* at the same size */
|
||||
if (g_ActiveConfig.stereo_mode != StereoMode::Off ||
|
||||
g_ActiveConfig.color_correction.bCorrectColorSpace ||
|
||||
g_ActiveConfig.color_correction.bCorrectGamma ||
|
||||
g_ActiveConfig.output_resampling_mode != OutputResamplingMode::Default ||
|
||||
m_backbuffer_format != AbstractTextureFormat::RGBA8 ||
|
||||
config.format != AbstractTextureFormat::RGBA8 || config.layers != 1 || config.levels != 1)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (source_rc.left != 0 || source_rc.top != 0 || target_rc.left != 0 || target_rc.top != 0 ||
|
||||
target_rc.GetWidth() != width || target_rc.GetHeight() != height ||
|
||||
width != m_backbuffer_width || height != m_backbuffer_height)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!g_gfx->PresentTexture(texture, static_cast<u32>(width), static_cast<u32>(height)))
|
||||
return false;
|
||||
|
||||
ReleaseHandedEntry();
|
||||
m_handed_entry = m_xfb_entry;
|
||||
return true;
|
||||
}
|
||||
|
||||
void Presenter::ReleaseHandedEntry()
|
||||
{
|
||||
if (m_handed_locked)
|
||||
m_handed_entry->ReleaseContentLock();
|
||||
m_handed_locked = false;
|
||||
m_handed_entry.reset();
|
||||
}
|
||||
|
||||
void Presenter::DoState(PointerWrap& p)
|
||||
{
|
||||
p.Do(m_frame_count);
|
||||
|
||||
@@ -80,6 +80,11 @@ public:
|
||||
|
||||
void ReleaseXFBContentLock();
|
||||
|
||||
/* Hands the frontend the XFB texture itself when drawing it would be a
|
||||
* plain copy of all of it onto all of the backbuffer; false when that
|
||||
* takes a draw, or the backend only hands on its own targets. */
|
||||
bool PresentXFBInPlace();
|
||||
|
||||
// Draws the specified XFB buffer to the screen, performing any post-processing.
|
||||
// Assumes that the backbuffer has already been bound and cleared.
|
||||
virtual void RenderXFBToScreen(const MathUtil::Rectangle<int>& target_rc,
|
||||
@@ -136,6 +141,15 @@ private:
|
||||
// Internal resolution multiplier scaled XFB size
|
||||
MathUtil::Rectangle<int> m_xfb_rect{0, 0, MAX_XFB_WIDTH, MAX_XFB_HEIGHT};
|
||||
|
||||
/* The XFB copy last handed on in place, while the frontend shows it.
|
||||
* Fields that repeat it keep it as m_xfb_entry, locked; fields that
|
||||
* blank the screen present nothing, so it is locked here until a
|
||||
* picture replaces it. */
|
||||
RcTcacheEntry m_handed_entry;
|
||||
bool m_handed_locked = false;
|
||||
|
||||
void ReleaseHandedEntry();
|
||||
|
||||
// Tracking of XFB textures so we don't render duplicate frames.
|
||||
u64 m_last_xfb_id = std::numeric_limits<u64>::max();
|
||||
|
||||
|
||||
Reference in new issue
Block a user