Full framebuffer emulation. Renderer interface changes

Helps for:
Densha de Go! 2, Issue #171
Vigilante 8, Issue #275
Xtreme Sports
Sonic Shuffle
The Ring of the Nibelungen
This commit is contained in:
Flyinghead 2022-10-23 16:32:42 +02:00
parent e3c260f4ca
commit 5722dc90f0
61 changed files with 1767 additions and 1108 deletions

View File

@ -473,8 +473,13 @@ if(UNIX AND NOT APPLE AND NOT ANDROID)
endif()
if(ASAN)
target_compile_options(${PROJECT_NAME} PRIVATE -fsanitize=address -static-libasan)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address -static-libasan")
if(ANDROID)
target_compile_options(${PROJECT_NAME} PRIVATE -fsanitize=address -fno-omit-frame-pointer)
set_target_properties(${PROJECT_NAME} PROPERTIES LINK_FLAGS -fsanitize=address)
else()
target_compile_options(${PROJECT_NAME} PRIVATE -fsanitize=address -static-libasan)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=address -static-libasan")
endif()
endif()
if(ANDROID AND NOT LIBRETRO)

View File

@ -104,6 +104,7 @@ Option<bool> ThreadedRendering("rend.ThreadedRendering", true);
Option<bool> DupeFrames("rend.DupeFrames", false);
Option<int> PerPixelLayers("rend.PerPixelLayers", 32);
Option<bool> NativeDepthInterpolation("rend.NativeDepthInterpolation", false);
Option<bool> EmulateFramebuffer("rend.EmulateFramebuffer", false);
// Misc

View File

@ -466,6 +466,7 @@ extern Option<int> TextureFiltering; // 0: default, 1: force nearest, 2: force l
extern Option<bool> ThreadedRendering;
extern Option<bool> DupeFrames;
extern Option<bool> NativeDepthInterpolation;
extern Option<bool> EmulateFramebuffer;
// Misc

View File

@ -223,6 +223,12 @@ static void loadSpecialSettings()
NOTICE_LOG(BOOT, "Forcing PAL broadcasting");
config::Broadcast.override(1);
}
if (prod_id == "T1102M" // Densha de Go! 2
|| prod_id == "T00000A") // The Ring of the Nibelungen (demo, hack)
{
NOTICE_LOG(BOOT, "Forcing Full Framebuffer Emulation");
config::EmulateFramebuffer.override(true);
}
}
else if (settings.platform.isArcade())
{
@ -764,11 +770,12 @@ void Emulator::run()
void Emulator::start()
{
if (state == Running)
return;
verify(state == Loaded);
state = Running;
SetMemoryHandlers();
settings.aica.NoBatch = config::ForceWindowsCE || config::DSPEnabled || config::GGPOEnable;
rend_resize_renderer();
#if FEAT_SHREC != DYNAREC_NONE
if (config::DynarecEnabled)
{
@ -829,11 +836,10 @@ bool Emulator::checkStatus()
bool Emulator::render()
{
rend_resize_renderer_if_needed();
if (state != Running)
return false;
if (!config::ThreadedRendering)
{
if (state != Running)
return false;
run();
// TODO if stopping due to a user request, no frame has been rendered
return !renderTimeout;

View File

@ -2,12 +2,15 @@
#include "spg.h"
#include "hw/pvr/pvr_mem.h"
#include "rend/TexCache.h"
#include "rend/transform_matrix.h"
#include "cfg/option.h"
#include "network/ggpo.h"
#include "emulator.h"
#include "serialize.h"
#include "hw/holly/holly_intc.h"
#include <mutex>
#include <deque>
void retro_rend_present();
#ifndef LIBRETRO
@ -17,15 +20,13 @@ void retro_rend_present()
sh4_cpu.Stop();
}
#endif
void retro_resize_renderer(int w, int h);
u32 VertexCount=0;
u32 FrameCount=1;
Renderer* renderer;
cResetEvent rs, re;
static bool do_swap;
std::mutex swap_mutex;
static cResetEvent renderEnd;
u32 fb_w_cur = 1;
static cResetEvent vramRollback;
@ -38,74 +39,195 @@ bool fb_dirty;
static bool pend_rend;
TA_context* _pvrrc;
extern bool rend_needs_resize;
static bool rend_frame(TA_context* ctx)
static bool presented;
class PvrMessageQueue
{
bool proc = renderer->Process(ctx);
using lock_guard = std::lock_guard<std::mutex>;
if (!proc || (!ctx->rend.isRTT && !ctx->rend.isRenderFramebuffer))
// If rendering to texture, continue locking until the frame is rendered
re.Set();
rend_allow_rollback();
public:
enum MessageType { NoMessage = -1, Render, RenderFramebuffer, Present, Stop };
struct Message
{
Message() = default;
Message(MessageType type, FramebufferInfo config)
: type(type), config(config) {}
return proc && renderer->Render();
}
MessageType type = NoMessage;
FramebufferInfo config;
};
void enqueue(MessageType type, FramebufferInfo config = FramebufferInfo())
{
Message msg { type, config };
if (config::ThreadedRendering)
{
// FIXME need some synchronization to avoid blinking in densha de go
// or use !threaded rendering for emufb?
// or read framebuffer vram on emu thread
bool dupe;
do {
dupe = false;
{
const lock_guard lock(mutex);
for (const auto& m : queue)
if (m.type == type) {
dupe = true;
break;
}
if (!dupe)
queue.push_back(msg);
}
if (dupe)
{
if (type == Stop)
return;
dequeueEvent.Wait();
}
} while (dupe);
enqueueEvent.Set();
}
else
{
// drain the queue after switching to !threaded rendering
while (!queue.empty())
waitAndExecute();
execute(msg);
}
}
bool waitAndExecute(int timeoutMs = -1)
{
return execute(dequeue(timeoutMs));
}
void reset() {
const lock_guard lock(mutex);
queue.clear();
}
void cancelEnqueue()
{
const lock_guard lock(mutex);
for (auto it = queue.begin(); it != queue.end(); )
{
if (it->type != Render)
it = queue.erase(it);
else
++it;
}
dequeueEvent.Set();
}
private:
Message dequeue(int timeoutMs = -1)
{
Message msg;
while (true)
{
{
const lock_guard lock(mutex);
if (!queue.empty())
{
msg = queue.front();
queue.pop_front();
}
}
if (msg.type != NoMessage) {
dequeueEvent.Set();
break;
}
if (timeoutMs == -1)
enqueueEvent.Wait();
else if (!enqueueEvent.Wait(timeoutMs))
break;
}
return msg;
}
bool execute(Message msg)
{
switch (msg.type)
{
case Render:
render();
break;
case RenderFramebuffer:
renderFramebuffer(msg.config);
break;
case Present:
present();
break;
case Stop:
return false;
default:
break;
}
return true;
}
void render()
{
_pvrrc = DequeueRender();
if (_pvrrc == nullptr)
return;
bool renderToScreen = !_pvrrc->rend.isRTT && !config::EmulateFramebuffer;
#ifdef LIBRETRO
if (renderToScreen)
retro_resize_renderer(_pvrrc->rend.framebufferWidth, _pvrrc->rend.framebufferHeight);
#endif
bool proc = renderer->Process(_pvrrc);
if (!proc || renderToScreen)
// If rendering to texture or in full framebuffer emulation, continue locking until the frame is rendered
renderEnd.Set();
rend_allow_rollback();
if (proc)
{
renderer->Render();
if (!renderToScreen)
renderEnd.Set();
}
//clear up & free data ..
FinishRender(_pvrrc);
_pvrrc = nullptr;
}
void renderFramebuffer(const FramebufferInfo& config)
{
#ifdef LIBRETRO
int w, h;
getTAViewport(w, h); // FIXME ?
retro_resize_renderer(w, h);
#endif
renderer->RenderFramebuffer(config);
}
void present()
{
if (renderer->Present())
{
presented = true;
retro_rend_present();
}
}
std::mutex mutex;
cResetEvent enqueueEvent;
cResetEvent dequeueEvent;
std::deque<Message> queue;
};
static PvrMessageQueue pvrQueue;
bool rend_single_frame(const bool& enabled)
{
do
{
if (config::ThreadedRendering && !rs.Wait(50))
presented = false;
while (enabled && !presented)
if (!pvrQueue.waitAndExecute(50))
return false;
if (do_swap)
{
do_swap = false;
if (renderer->Present())
{
rs.Set(); // don't miss any render
retro_rend_present();
return true;
}
}
if (!enabled)
return false;
_pvrrc = DequeueRender();
if (!config::ThreadedRendering && _pvrrc == nullptr)
return false;
}
while (_pvrrc == nullptr);
bool frame_rendered = rend_frame(_pvrrc);
if (frame_rendered)
{
{
std::lock_guard<std::mutex> lock(swap_mutex);
if (config::DelayFrameSwapping && !_pvrrc->rend.isRenderFramebuffer && fb_w_cur != FB_R_SOF1 && !do_swap)
// Delay swap
frame_rendered = false;
else
// Swap now
do_swap = false;
}
if (frame_rendered)
{
frame_rendered = renderer->Present();
if (frame_rendered)
retro_rend_present();
}
}
if (_pvrrc->rend.isRTT)
re.Set();
//clear up & free data ..
FinishRender(_pvrrc);
_pvrrc = nullptr;
return frame_rendered;
return true;
}
Renderer* rend_GLES2();
@ -181,99 +303,107 @@ void rend_term_renderer()
void rend_reset()
{
FinishRender(DequeueRender());
do_swap = false;
render_called = false;
pend_rend = false;
FrameCount = 1;
VertexCount = 0;
fb_w_cur = 1;
pvrQueue.reset();
}
void rend_start_render(TA_context *ctx)
void rend_start_render()
{
render_called = true;
pend_rend = false;
if (ctx == nullptr)
TA_context *ctx = nullptr;
u32 addresses[MAX_PASSES];
int count = getTAContextAddresses(addresses);
if (count > 0)
{
u32 addresses[MAX_PASSES];
int count = getTAContextAddresses(addresses);
if (count > 0)
ctx = tactx_Pop(addresses[0]);
if (ctx != nullptr)
{
ctx = tactx_Pop(addresses[0]);
if (ctx != nullptr)
TA_context *linkedCtx = ctx;
for (int i = 1; i < count; i++)
{
TA_context *linkedCtx = ctx;
for (int i = 1; i < count; i++)
{
linkedCtx->nextContext = tactx_Pop(addresses[i]);
if (linkedCtx->nextContext != nullptr)
linkedCtx = linkedCtx->nextContext;
}
linkedCtx->nextContext = tactx_Pop(addresses[i]);
if (linkedCtx->nextContext != nullptr)
linkedCtx = linkedCtx->nextContext;
}
}
}
// No end of render interrupt when rendering the framebuffer
if (!ctx || !ctx->rend.isRenderFramebuffer)
SetREP(ctx);
scheduleRenderDone(ctx);
if (ctx)
if (ctx == nullptr)
return;
FillBGP(ctx);
ctx->rend.isRTT = (FB_W_SOF1 & 0x1000000) != 0;
ctx->rend.fb_W_SOF1 = FB_W_SOF1;
ctx->rend.fb_W_CTRL.full = FB_W_CTRL.full;
ctx->rend.fb_X_CLIP = FB_X_CLIP;
ctx->rend.fb_Y_CLIP = FB_Y_CLIP;
ctx->rend.fb_W_LINESTRIDE = FB_W_LINESTRIDE.stride;
ctx->rend.fog_clamp_min = FOG_CLAMP_MIN;
ctx->rend.fog_clamp_max = FOG_CLAMP_MAX;
if (!ctx->rend.isRTT)
{
if (ctx->rend.isRenderFramebuffer)
{
ctx->rend.isRTT = false;
ctx->rend.fb_X_CLIP.min = 0;
ctx->rend.fb_X_CLIP.max = 639;
ctx->rend.fb_Y_CLIP.min = 0;
ctx->rend.fb_Y_CLIP.max = 479;
int width, height;
getScaledFramebufferSize(width, height);
ctx->rend.framebufferWidth = width;
ctx->rend.framebufferHeight = height;
}
ctx->rend.fog_clamp_min.full = 0;
ctx->rend.fog_clamp_max.full = 0xffffffff;
}
else
{
FillBGP(ctx);
ctx->rend.isRTT = (FB_W_SOF1 & 0x1000000) != 0;
ctx->rend.fb_W_SOF1 = FB_W_SOF1;
ctx->rend.fb_W_CTRL.full = FB_W_CTRL.full;
ctx->rend.fb_X_CLIP = FB_X_CLIP;
ctx->rend.fb_Y_CLIP = FB_Y_CLIP;
ctx->rend.fb_W_LINESTRIDE = FB_W_LINESTRIDE.stride;
ctx->rend.fog_clamp_min = FOG_CLAMP_MIN;
ctx->rend.fog_clamp_max = FOG_CLAMP_MAX;
}
if (!config::DelayFrameSwapping && !ctx->rend.isRTT)
ggpo::endOfFrame();
bool present = !config::DelayFrameSwapping && !ctx->rend.isRTT && !config::EmulateFramebuffer;
if (present)
ggpo::endOfFrame();
if (QueueRender(ctx))
{
palette_update();
if (QueueRender(ctx))
{
pend_rend = true;
if (!config::ThreadedRendering)
rend_single_frame(true);
else
rs.Set();
}
pend_rend = true;
pvrQueue.enqueue(PvrMessageQueue::Render);
if (present)
pvrQueue.enqueue(PvrMessageQueue::Present);
}
}
void rend_end_render()
int rend_end_render(int tag, int cycles, int jitter)
{
if (settings.platform.isNaomi2())
{
asic_RaiseInterruptBothCLX(holly_RENDER_DONE);
asic_RaiseInterruptBothCLX(holly_RENDER_DONE_isp);
asic_RaiseInterruptBothCLX(holly_RENDER_DONE_vd);
}
else
{
asic_RaiseInterrupt(holly_RENDER_DONE);
asic_RaiseInterrupt(holly_RENDER_DONE_isp);
asic_RaiseInterrupt(holly_RENDER_DONE_vd);
}
if (pend_rend && config::ThreadedRendering)
re.Wait();
renderEnd.Wait();
return 0;
}
void rend_vblank()
{
if (!render_called && fb_dirty && FB_R_CTRL.fb_enable)
if (config::EmulateFramebuffer
|| (!render_called && fb_dirty && FB_R_CTRL.fb_enable))
{
DEBUG_LOG(PVR, "Direct framebuffer write detected");
TA_context *ctx = tactx_Alloc();
ctx->rend.isRenderFramebuffer = true;
rend_start_render(ctx);
FramebufferInfo fbInfo;
fbInfo.update();
pvrQueue.enqueue(PvrMessageQueue::RenderFramebuffer, fbInfo);
pvrQueue.enqueue(PvrMessageQueue::Present);
ggpo::endOfFrame();
if (!config::EmulateFramebuffer)
DEBUG_LOG(PVR, "Direct framebuffer write detected");
fb_dirty = false;
}
render_called = false;
@ -293,8 +423,12 @@ void rend_cancel_emu_wait()
if (config::ThreadedRendering)
{
FinishRender(NULL);
re.Set();
renderEnd.Set();
rend_allow_rollback();
pvrQueue.cancelEnqueue();
// Needed for android where this function may be called
// from a thread different from the UI one
pvrQueue.enqueue(PvrMessageQueue::Stop);
}
}
@ -308,22 +442,12 @@ void rend_set_fb_write_addr(u32 fb_w_sof1)
void rend_swap_frame(u32 fb_r_sof)
{
swap_mutex.lock();
if (fb_r_sof == fb_w_cur)
if (!config::EmulateFramebuffer && fb_r_sof == fb_w_cur)
{
do_swap = true;
if (config::ThreadedRendering)
rs.Set();
else
{
swap_mutex.unlock();
rend_single_frame(true);
swap_mutex.lock();
}
pvrQueue.enqueue(PvrMessageQueue::Present);
if (config::DelayFrameSwapping)
ggpo::endOfFrame();
}
swap_mutex.unlock();
}
void rend_disable_rollback()
@ -364,42 +488,4 @@ void rend_deserialize(Deserializer& deser)
deser >> fb_watch_addr_end;
}
pend_rend = false;
rend_needs_resize = true;
}
void rend_resize_renderer()
{
int fbwidth = 640 / (1 + VO_CONTROL.pixel_double) * (1 + SCALER_CTL.hscale);
int fbheight = FB_R_CTRL.vclk_div == 1 || SPG_CONTROL.interlace == 1 ? 480 : 240;
if (SPG_CONTROL.interlace == 0 && SCALER_CTL.vscalefactor > 0x400)
fbheight *= std::roundf((float)SCALER_CTL.vscalefactor / 0x400);
float upscaling = config::RenderResolution / 480.f;
float hres = fbwidth * upscaling;
float vres = fbheight * upscaling;
if (config::Widescreen && !config::Rotate90)
{
if (config::SuperWidescreen)
hres *= (float)settings.display.width / settings.display.height / 4.f * 3.f;
else
hres *= 4.f / 3.f;
}
if (!config::Rotate90)
hres = std::roundf(hres / 2.f) * 2.f;
DEBUG_LOG(RENDERER, "rend_resize_renderer: %d x %d", (int)hres, (int)vres);
if (renderer != nullptr)
renderer->Resize((int)hres, (int)vres);
rend_needs_resize = false;
#ifdef LIBRETRO
void retro_resize_renderer(int w, int h);
retro_resize_renderer((int)hres, (int)vres);
#endif
}
void rend_resize_renderer_if_needed()
{
if (!rend_needs_resize)
return;
rend_resize_renderer();
}

View File

@ -2,14 +2,13 @@
#include "types.h"
#include "ta_ctx.h"
extern u32 VertexCount;
extern u32 FrameCount;
void rend_init_renderer();
void rend_term_renderer();
void rend_vblank();
void rend_start_render(TA_context *ctx = nullptr);
void rend_end_render();
void rend_start_render();
int rend_end_render(int tag, int cycles, int jitter);
void rend_cancel_emu_wait();
bool rend_single_frame(const bool& enabled);
void rend_swap_frame(u32 fb_r_sof1);
@ -20,25 +19,46 @@ void rend_start_rollback();
void rend_allow_rollback();
void rend_serialize(Serializer& ser);
void rend_deserialize(Deserializer& deser);
void rend_resize_renderer();
void rend_resize_renderer_if_needed();
///////
extern TA_context* _pvrrc;
#define pvrrc (_pvrrc->rend)
struct FramebufferInfo
{
void update()
{
fb_r_size.full = FB_R_SIZE.full;
fb_r_ctrl.full = FB_R_CTRL.full;
spg_control.full = SPG_CONTROL.full;
spg_status.full = SPG_STATUS.full;
fb_r_sof1 = FB_R_SOF1;
fb_r_sof2 = FB_R_SOF2;
vo_control.full = VO_CONTROL.full;
vo_border_col.full = VO_BORDER_COL.full;
}
FB_R_SIZE_type fb_r_size;
FB_R_CTRL_type fb_r_ctrl;
SPG_CONTROL_type spg_control;
SPG_STATUS_type spg_status;
u32 fb_r_sof1;
u32 fb_r_sof2;
VO_CONTROL_type vo_control;
VO_BORDER_COL_type vo_border_col;
};
struct Renderer
{
virtual bool Init()=0;
virtual ~Renderer() = default;
virtual void Resize(int w, int h)=0;
virtual void Term()=0;
virtual bool Init() = 0;
virtual void Term() = 0;
virtual bool Process(TA_context* ctx)=0;
virtual bool Render()=0;
virtual bool Process(TA_context *ctx) = 0;
virtual bool Render() = 0;
virtual void RenderFramebuffer(const FramebufferInfo& info) = 0;
virtual bool RenderLastFrame() { return false; }
virtual bool Present() { return true; }

View File

@ -7,7 +7,6 @@
bool pal_needs_update=true;
bool fog_needs_update=true;
bool rend_needs_resize = true;
u8 pvr_regs[pvr_RegSize];
@ -160,16 +159,6 @@ void pvr_WriteReg(u32 paddr,u32 data)
{
PvrReg(addr, u32) = data;
CalculateSync();
if (addr == SPG_CONTROL_addr)
rend_needs_resize = true;
}
return;
case VO_CONTROL_addr:
if (PvrReg(addr, u32) != data)
{
PvrReg(addr, u32) = data;
rend_needs_resize = true;
}
return;
@ -178,10 +167,7 @@ void pvr_WriteReg(u32 paddr,u32 data)
bool vclk_div_changed = (PvrReg(addr, u32) ^ data) & (1 << 23);
PvrReg(addr, u32) = data;
if (vclk_div_changed)
{
CalculateSync();
rend_needs_resize = true;
}
}
return;

View File

@ -122,14 +122,14 @@ struct RGBAColorTemplate
float blue() const { return ((T *)this)->_blue / 255.f; }
float alpha() const { return ((T *)this)->_alpha / 255.f; }
void getRGBColor(float rgb[3])
void getRGBColor(float rgb[3]) const
{
rgb[0] = red();
rgb[1] = green();
rgb[2] = blue();
}
void getRGBAColor(float rgba[4])
void getRGBAColor(float rgba[4]) const
{
getRGBColor(rgba);
rgba[3] = alpha();

View File

@ -56,7 +56,7 @@ void CalculateSync()
}
//called from sh4 context , should update pvr/ta state and everything else
int spg_line_sched(int tag, int cycl, int jit)
static int spg_line_sched(int tag, int cycl, int jit)
{
clc_pvr_scanline += cycl;
@ -146,22 +146,9 @@ int spg_line_sched(int tag, int cycl, int jit)
double spd_cpu=spd_vbs*Frame_Cycles;
spd_cpu/=1000000; //mrhz kthx
double fullvbs=(spd_vbs/spd_cpu)*200;
double mv=VertexCount/ts/(spd_cpu/200);
char mv_c=' ';
Last_FC=FrameCount;
if (mv>750)
{
mv/=1000; //KV
mv_c='K';
}
if (mv>750)
{
mv/=1000; //
mv_c='M';
}
VertexCount=0;
vblk_cnt=0;
const char* mode=0;
@ -184,11 +171,10 @@ int spg_line_sched(int tag, int cycl, int jit)
double full_rps = spd_fps + fskip / ts;
INFO_LOG(COMMON, "%s/%c - %4.2f - %4.2f - V: %4.2f (%.2f, %s%s%4.2f) R: %4.2f+%4.2f VTX: %4.2f%c",
INFO_LOG(COMMON, "%s/%c - %4.2f - %4.2f - V: %4.2f (%.2f, %s%s%4.2f) R: %4.2f+%4.2f",
VER_SHORTNAME,'n',mspdf,spd_cpu*100/200,spd_vbs,
spd_vbs/full_rps,mode,res,fullvbs,
spd_fps,fskip/ts
, mv, mv_c);
spd_fps,fskip/ts);
fskip=0;
last_fps=os_GetSeconds();
@ -255,28 +241,10 @@ void read_lightgun_position(int x, int y)
}
}
int rend_end_sch(int tag, int cycl, int jitt)
{
if (settings.platform.isNaomi2())
{
asic_RaiseInterruptBothCLX(holly_RENDER_DONE);
asic_RaiseInterruptBothCLX(holly_RENDER_DONE_isp);
asic_RaiseInterruptBothCLX(holly_RENDER_DONE_vd);
}
else
{
asic_RaiseInterrupt(holly_RENDER_DONE);
asic_RaiseInterrupt(holly_RENDER_DONE_isp);
asic_RaiseInterrupt(holly_RENDER_DONE_vd);
}
rend_end_render();
return 0;
}
bool spg_Init()
{
render_end_schid=sh4_sched_register(0,&rend_end_sch);
vblank_schid=sh4_sched_register(0,&spg_line_sched);
render_end_schid = sh4_sched_register(0, &rend_end_render);
vblank_schid = sh4_sched_register(0, &spg_line_sched);
return true;
}
@ -299,7 +267,7 @@ void spg_Reset(bool hard)
real_times.fill(0.0);
}
void SetREP(TA_context* cntx)
void scheduleRenderDone(TA_context *cntx)
{
if (cntx)
sh4_sched_request(render_end_schid, 500000 * 3);

View File

@ -11,4 +11,4 @@ void spg_Deserialize(Deserializer& deser);
void CalculateSync();
void read_lightgun_position(int x, int y);
void SetREP(TA_context* cntx);
void scheduleRenderDone(TA_context *cntx);

View File

@ -5,7 +5,6 @@
#include "serialize.h"
extern u32 fskip;
extern u32 FrameCount;
static int RenderCount;
TA_context* ta_ctx;
@ -40,8 +39,8 @@ void SetCurrentTARC(u32 addr)
}
}
TA_context* rqueue;
cResetEvent frame_finished;
static TA_context* rqueue;
static cResetEvent frame_finished;
bool QueueRender(TA_context* ctx)
{
@ -86,10 +85,6 @@ TA_context* DequeueRender()
return rqueue;
}
bool rend_framePending() {
return rqueue != nullptr;
}
void FinishRender(TA_context* ctx)
{
if (ctx != nullptr)

View File

@ -6,8 +6,6 @@
#include "stdclass.h"
#include "oslib/oslib.h"
#include <mutex>
class BaseTextureCacheData;
struct N2LightModel;
@ -228,13 +226,14 @@ struct rend_context
bool Overrun;
bool isRTT;
bool isRenderFramebuffer;
FB_X_CLIP_type fb_X_CLIP;
FB_Y_CLIP_type fb_Y_CLIP;
u32 fb_W_LINESTRIDE;
u32 fb_W_SOF1;
FB_W_CTRL_type fb_W_CTRL;
u32 framebufferWidth;
u32 framebufferHeight;
RGBAColor fog_clamp_min;
RGBAColor fog_clamp_max;
@ -272,13 +271,13 @@ struct rend_context
Overrun = false;
fZ_min = 1000000.0f;
fZ_max = 1.0f;
isRenderFramebuffer = false;
matrices.Clear();
lightModels.Clear();
}
void newRenderPass();
// For RTT TODO merge with framebufferWidth/Height
u32 getFramebufferWidth() const {
u32 w = fb_X_CLIP.max + 1;
if (fb_W_LINESTRIDE != 0)
@ -298,8 +297,6 @@ struct TA_context
{
u32 Address;
std::mutex rend_inuse;
tad_context tad;
rend_context rend;
@ -352,10 +349,8 @@ struct TA_context
verify(tad.End() - tad.thd_root <= TA_DATA_SIZE);
tad.Clear();
nextContext = nullptr;
rend_inuse.lock();
rend.Clear();
rend.proc_end = rend.proc_start = tad.thd_root;
rend_inuse.unlock();
}
~TA_context()
@ -398,7 +393,6 @@ void FinishRender(TA_context* ctx);
//must be moved to proper header
void FillBGP(TA_context* ctx);
bool rend_framePending();
void SerializeTAContext(Serializer& ser);
void DeserializeTAContext(Deserializer& deser);

View File

@ -1276,7 +1276,6 @@ static void fix_texture_bleeding(const List<PolyParam> *list)
static bool ta_parse_vdrc(TA_context* ctx)
{
ctx->rend_inuse.lock();
bool rv=false;
verify(vd_ctx == nullptr);
vd_ctx = ctx;
@ -1352,7 +1351,7 @@ static bool ta_parse_vdrc(TA_context* ctx)
bool overrun = vd_ctx->rend.Overrun;
if (overrun)
WARN_LOG(PVR, "ERROR: TA context overrun");
else if (config::RenderResolution > 480)
else if (config::RenderResolution > 480 && !config::EmulateFramebuffer)
{
fix_texture_bleeding(&vd_rc.global_param_op);
fix_texture_bleeding(&vd_rc.global_param_pt);
@ -1369,7 +1368,6 @@ static bool ta_parse_vdrc(TA_context* ctx)
}
vd_ctx = nullptr;
ctx->rend_inuse.unlock();
ctx->rend.Overrun = overrun;
@ -1378,8 +1376,6 @@ static bool ta_parse_vdrc(TA_context* ctx)
static bool ta_parse_naomi2(TA_context* ctx)
{
ctx->rend_inuse.lock();
for (PolyParam& pp : ctx->rend.global_param_op)
{
if (pp.pcw.Texture)
@ -1434,7 +1430,6 @@ static bool ta_parse_naomi2(TA_context* ctx)
ctx->rend.fb_Y_CLIP.min = std::max(ctx->rend.fb_Y_CLIP.min, ymin);
ctx->rend.fb_Y_CLIP.max = std::min(ctx->rend.fb_Y_CLIP.max, ymax + 31);
}
ctx->rend_inuse.unlock();
return !overrun;
}

View File

@ -812,14 +812,14 @@ void BaseTextureCacheData::SetDirectXColorOrder(bool enabled) {
}
template<typename Packer>
void ReadFramebuffer(PixelBuffer<u32>& pb, int& width, int& height)
void ReadFramebuffer(const FramebufferInfo& info, PixelBuffer<u32>& pb, int& width, int& height)
{
width = (FB_R_SIZE.fb_x_size + 1) << 1; // in 16-bit words
height = FB_R_SIZE.fb_y_size + 1;
int modulus = (FB_R_SIZE.fb_modulus - 1) << 1;
width = (info.fb_r_size.fb_x_size + 1) * 2; // in 16-bit words
height = info.fb_r_size.fb_y_size + 1;
int modulus = (info.fb_r_size.fb_modulus - 1) * 2;
int bpp;
switch (FB_R_CTRL.fb_depth)
switch (info.fb_r_ctrl.fb_depth)
{
case fbde_0555:
case fbde_565:
@ -841,10 +841,10 @@ void ReadFramebuffer(PixelBuffer<u32>& pb, int& width, int& height)
break;
}
u32 addr = FB_R_SOF1;
if (SPG_CONTROL.interlace)
u32 addr = info.fb_r_sof1;
if (info.spg_control.interlace)
{
if (width == modulus && FB_R_SOF2 == FB_R_SOF1 + width * bpp)
if (width == modulus && info.fb_r_sof2 == info.fb_r_sof1 + width * bpp)
{
// Typical case alternating even and odd lines -> take the whole buffer at once
modulus = 0;
@ -852,14 +852,15 @@ void ReadFramebuffer(PixelBuffer<u32>& pb, int& width, int& height)
}
else
{
addr = SPG_STATUS.fieldnum ? FB_R_SOF2 : FB_R_SOF1;
addr = info.spg_status.fieldnum ? info.fb_r_sof2 : info.fb_r_sof1;
}
}
pb.init(width, height);
u32 *dst = (u32 *)pb.data();
const u32 fb_concat = info.fb_r_ctrl.fb_concat;
switch (FB_R_CTRL.fb_depth)
switch (info.fb_r_ctrl.fb_depth)
{
case fbde_0555: // 555 RGB
for (int y = 0; y < height; y++)
@ -868,9 +869,9 @@ void ReadFramebuffer(PixelBuffer<u32>& pb, int& width, int& height)
{
u16 src = pvr_read32p<u16>(addr);
*dst++ = Packer::pack(
(((src >> 10) & 0x1F) << 3) + FB_R_CTRL.fb_concat,
(((src >> 5) & 0x1F) << 3) + FB_R_CTRL.fb_concat,
(((src >> 0) & 0x1F) << 3) + FB_R_CTRL.fb_concat,
(((src >> 10) & 0x1F) << 3) | fb_concat,
(((src >> 5) & 0x1F) << 3) | fb_concat,
(((src >> 0) & 0x1F) << 3) | fb_concat,
0xff);
addr += bpp;
}
@ -885,9 +886,9 @@ void ReadFramebuffer(PixelBuffer<u32>& pb, int& width, int& height)
{
u16 src = pvr_read32p<u16>(addr);
*dst++ = Packer::pack(
(((src >> 11) & 0x1F) << 3) + FB_R_CTRL.fb_concat,
(((src >> 5) & 0x3F) << 2) + (FB_R_CTRL.fb_concat & 3),
(((src >> 0) & 0x1F) << 3) + FB_R_CTRL.fb_concat,
(((src >> 11) & 0x1F) << 3) | fb_concat,
(((src >> 5) & 0x3F) << 2) | (fb_concat & 3),
(((src >> 0) & 0x1F) << 3) | fb_concat,
0xFF);
addr += bpp;
}
@ -933,11 +934,11 @@ void ReadFramebuffer(PixelBuffer<u32>& pb, int& width, int& height)
break;
}
}
template void ReadFramebuffer<RGBAPacker>(PixelBuffer<u32>& pb, int& width, int& height);
template void ReadFramebuffer<BGRAPacker>(PixelBuffer<u32>& pb, int& width, int& height);
template void ReadFramebuffer<RGBAPacker>(const FramebufferInfo& info, PixelBuffer<u32>& pb, int& width, int& height);
template void ReadFramebuffer<BGRAPacker>(const FramebufferInfo& info, PixelBuffer<u32>& pb, int& width, int& height);
template<int Red, int Green, int Blue, int Alpha>
void WriteTextureToVRam(u32 width, u32 height, u8 *data, u16 *dst, FB_W_CTRL_type fb_w_ctrl, u32 linestride)
void WriteTextureToVRam(u32 width, u32 height, const u8 *data, u16 *dst, FB_W_CTRL_type fb_w_ctrl, u32 linestride)
{
u32 padding = linestride;
if (padding / 2 > width)
@ -948,7 +949,7 @@ void WriteTextureToVRam(u32 width, u32 height, u8 *data, u16 *dst, FB_W_CTRL_typ
const u16 kval_bit = (fb_w_ctrl.fb_kval & 0x80) << 8;
const u8 fb_alpha_threshold = fb_w_ctrl.fb_alpha_threshold;
u8 *p = data;
const u8 *p = data;
for (u32 l = 0; l < height; l++) {
switch(fb_w_ctrl.fb_packmode)
@ -981,8 +982,139 @@ void WriteTextureToVRam(u32 width, u32 height, u8 *data, u16 *dst, FB_W_CTRL_typ
dst += padding;
}
}
template void WriteTextureToVRam<0, 1, 2, 3>(u32 width, u32 height, u8 *data, u16 *dst, FB_W_CTRL_type fb_w_ctrl, u32 linestride);
template void WriteTextureToVRam<2, 1, 0, 3>(u32 width, u32 height, u8 *data, u16 *dst, FB_W_CTRL_type fb_w_ctrl, u32 linestride);
template void WriteTextureToVRam<0, 1, 2, 3>(u32 width, u32 height, const u8 *data, u16 *dst, FB_W_CTRL_type fb_w_ctrl, u32 linestride);
template void WriteTextureToVRam<2, 1, 0, 3>(u32 width, u32 height, const u8 *data, u16 *dst, FB_W_CTRL_type fb_w_ctrl, u32 linestride);
template<int bits>
static inline u8 roundColor(u8 in)
{
u8 out = in >> (8 - bits);
if (out != 0xffu >> (8 - bits))
out += (in >> (8 - bits - 1)) & 1;
return out;
}
template<int Red, int Green, int Blue, int Alpha>
void WriteFramebuffer(u32 width, u32 height, const u8 *data, u32 dstAddr, FB_W_CTRL_type fb_w_ctrl, u32 linestride, FB_X_CLIP_type xclip, FB_Y_CLIP_type yclip)
{
int bpp;
switch (fb_w_ctrl.fb_packmode)
{
case 0:
case 1:
case 2:
case 3:
bpp = 2;
break;
case 4:
bpp = 3;
break;
case 5:
case 6:
bpp = 4;
break;
default:
die("Invalid framebuffer format");
bpp = 4;
break;
}
u32 padding = linestride;
if (padding > width * bpp)
padding = padding - width * bpp;
else
padding = 0;
const u16 kval_bit = (fb_w_ctrl.fb_kval & 0x80) << 8;
const u8 fb_alpha_threshold = fb_w_ctrl.fb_alpha_threshold;
const u8 *p = data + 4 * yclip.min * width;
dstAddr += bpp * yclip.min * (width + padding / bpp);
for (u32 l = yclip.min; l < height && l <= yclip.max; l++)
{
p += 4 * xclip.min;
dstAddr += bpp * xclip.min;
switch(fb_w_ctrl.fb_packmode)
{
case 0: // 0555 KRGB 16 bit (default) Bit 15 is the value of fb_kval[7].
for (u32 c = xclip.min; c < width && c <= xclip.max; c++) {
pvr_write32p(dstAddr, (u16)((roundColor<5>(p[Red]) << 10)
| (roundColor<5>(p[Green]) << 5)
| roundColor<5>(p[Blue])
| kval_bit));
p += 4;
dstAddr += bpp;
}
break;
case 1: // 565 RGB 16 bit
for (u32 c = xclip.min; c < width && c <= xclip.max; c++) {
pvr_write32p(dstAddr, (u16)((roundColor<5>(p[Red]) << 11)
| (roundColor<6>(p[Green]) << 5)
| roundColor<5>(p[Blue])));
p += 4;
dstAddr += bpp;
}
break;
case 2: // 4444 ARGB 16 bit
for (u32 c = xclip.min; c < width && c <= xclip.max; c++) {
pvr_write32p(dstAddr, (u16)((roundColor<4>(p[Red]) << 8)
| (roundColor<4>(p[Green]) << 4)
| roundColor<4>(p[Blue])
| (roundColor<4>(p[Alpha]) << 12)));
p += 4;
dstAddr += bpp;
}
break;
case 3: // 1555 ARGB 16 bit The alpha value is determined by comparison with the value of fb_alpha_threshold.
for (u32 c = xclip.min; c < width && c <= xclip.max; c++) {
pvr_write32p(dstAddr, (u16)((roundColor<5>(p[Red]) << 10)
| (roundColor<5>(p[Green]) << 5)
| roundColor<5>(p[Blue])
| (p[Alpha] > fb_alpha_threshold ? 0x8000 : 0)));
p += 4;
dstAddr += bpp;
}
break;
case 4: // 888 RGB 24 bit packed
for (u32 c = xclip.min; c < width - 3u && c <= xclip.max - 3u; c += 4) {
pvr_write32p(dstAddr, (u32)((p[Blue + 4] << 24) | (p[Red] << 16) | (p[Green] << 8) | p[Blue]));
p += 4;
dstAddr += 4;
pvr_write32p(dstAddr, (u32)((p[Green + 4] << 24) | (p[Blue + 4] << 16) | (p[Red] << 8) | p[Green]));
p += 4;
dstAddr += 4;
pvr_write32p(dstAddr, (u32)((p[Red + 4] << 24) | (p[Green + 4] << 16) | (p[Blue + 4] << 8) | p[Red]));
p += 8;
dstAddr += 4;
}
break;
case 5: // 0888 KRGB 32 bit (K is the value of fk_kval.)
for (u32 c = xclip.min; c < width && c <= xclip.max; c++) {
pvr_write32p(dstAddr, (u32)((p[Red] << 16) | (p[Green] << 8) | p[Blue] | (fb_w_ctrl.fb_kval << 24)));
p += 4;
dstAddr += bpp;
}
break;
case 6: // 8888 ARGB 32 bit
for (u32 c = xclip.min; c < width && c <= xclip.max; c++) {
pvr_write32p(dstAddr, (u32)((p[Red] << 16) | (p[Green] << 8) | p[Blue] | (p[Alpha] << 24)));
p += 4;
dstAddr += bpp;
}
break;
default:
break;
}
dstAddr += padding + (width - xclip.max - 1) * bpp;
p += (width - xclip.max - 1) * 4;
}
}
template void WriteFramebuffer<0, 1, 2, 3>(u32 width, u32 height, const u8 *data, u32 dstAddr, FB_W_CTRL_type fb_w_ctrl,
u32 linestride, FB_X_CLIP_type xclip, FB_Y_CLIP_type yclip);
template void WriteFramebuffer<2, 1, 0, 3>(u32 width, u32 height, const u8 *data, u32 dstAddr, FB_W_CTRL_type fb_w_ctrl,
u32 linestride, FB_X_CLIP_type xclip, FB_Y_CLIP_type yclip);
void BaseTextureCacheData::invalidate()
{

View File

@ -798,9 +798,15 @@ protected:
};
template<typename Packer = RGBAPacker>
void ReadFramebuffer(PixelBuffer<u32>& pb, int& width, int& height);
void ReadFramebuffer(const FramebufferInfo& info, PixelBuffer<u32>& pb, int& width, int& height);
// width and height in pixels. linestride in bytes
template<int Red = 0, int Green = 1, int Blue = 2, int Alpha = 3>
void WriteTextureToVRam(u32 width, u32 height, u8 *data, u16 *dst, FB_W_CTRL_type fb_w_ctrl, u32 linestride);
void WriteFramebuffer(u32 width, u32 height, const u8 *data, u32 dstAddr, FB_W_CTRL_type fb_w_ctrl, u32 linestride, FB_X_CLIP_type xclip, FB_Y_CLIP_type yclip);
// width and height in pixels. linestride in bytes
template<int Red = 0, int Green = 1, int Blue = 2, int Alpha = 3>
void WriteTextureToVRam(u32 width, u32 height, const u8 *data, u16 *dst, FB_W_CTRL_type fb_w_ctrl, u32 linestride);
void getRenderToTextureDimensions(u32& width, u32& height, u32& pow2Width, u32& pow2Height);
static inline void MakeFogTexture(u8 *tex_data)

View File

@ -171,6 +171,9 @@ void DX11Renderer::Term()
fbTex.reset();
fbTextureView.reset();
fbRenderTarget.reset();
fbScaledRenderTarget.reset();
fbScaledTextureView.reset();
fbScaledTexture.reset();
quad.reset();
deviceContext.reset();
device.reset();
@ -233,7 +236,7 @@ void DX11Renderer::createTexAndRenderTarget(ComPtr<ID3D11Texture2D>& texture, Co
deviceContext->ClearRenderTargetView(renderTarget, black);
}
void DX11Renderer::Resize(int w, int h)
void DX11Renderer::resize(int w, int h)
{
if (width == (u32)w && height == (u32)h)
return;
@ -304,15 +307,7 @@ bool DX11Renderer::Process(TA_context* ctx)
texCache.Clear();
texCache.Cleanup();
if (ctx->rend.isRenderFramebuffer)
{
readDCFramebuffer();
return true;
}
else
{
return ta_parse(ctx);
}
return ta_parse(ctx);
}
void DX11Renderer::configVertexShader()
@ -413,6 +408,8 @@ void DX11Renderer::setupPixelShaderConstants()
bool DX11Renderer::Render()
{
resize(pvrrc.framebufferWidth, pvrrc.framebufferHeight);
// make sure to unbind the framebuffer view before setting it as render target
ID3D11ShaderResourceView *nullView = nullptr;
deviceContext->PSSetShaderResources(0, 1, &nullView);
@ -426,27 +423,24 @@ bool DX11Renderer::Render()
deviceContext->IASetInputLayout(mainInputLayout);
if (!pvrrc.isRenderFramebuffer)
{
n2Helper.resetCache();
uploadGeometryBuffers();
n2Helper.resetCache();
uploadGeometryBuffers();
updateFogTexture();
updatePaletteTexture();
updateFogTexture();
updatePaletteTexture();
setupPixelShaderConstants();
setupPixelShaderConstants();
drawStrips();
}
else
{
renderDCFramebuffer();
}
drawStrips();
if (is_rtt)
{
readRttRenderTarget(pvrrc.fb_W_SOF1 & VRAM_MASK);
}
else if (config::EmulateFramebuffer)
{
writeFramebufferToVRAM();
}
else
{
#ifndef LIBRETRO
@ -467,24 +461,6 @@ bool DX11Renderer::Render()
return !is_rtt;
}
void DX11Renderer::renderDCFramebuffer()
{
float colors[4];
VO_BORDER_COL.getRGBColor(colors);
colors[3] = 1.f;
deviceContext->ClearRenderTargetView(fbRenderTarget, colors);
D3D11_VIEWPORT vp{};
vp.Width = (FLOAT)width;
vp.Height = (FLOAT)height;
vp.MinDepth = 0.f;
vp.MaxDepth = 1.f;
deviceContext->RSSetViewports(1, &vp);
deviceContext->OMSetBlendState(blendStates.getState(false), nullptr, 0xffffffff);
float bar = (width - height * 640.f / 480.f) / 2.f;
quad->draw(dcfbTextureView, samplers->getSampler(true), nullptr, bar / width * 2.f - 1.f, -1.f, (width - bar * 2.f) / width * 2.f, 2.f);
}
void DX11Renderer::renderFramebuffer()
{
#ifndef LIBRETRO
@ -899,18 +875,30 @@ bool DX11Renderer::RenderLastFrame()
if (!frameRenderedOnce)
return false;
renderFramebuffer();
return false;
return true;
}
void DX11Renderer::readDCFramebuffer()
void DX11Renderer::RenderFramebuffer(const FramebufferInfo& info)
{
if (FB_R_SIZE.fb_x_size == 0 || FB_R_SIZE.fb_y_size == 0)
return;
PixelBuffer<u32> pb;
int width;
int height;
ReadFramebuffer<BGRAPacker>(pb, width, height);
if (info.fb_r_ctrl.fb_enable == 0 || info.vo_control.blank_video == 1)
{
// Video output disabled
width = height = 1;
pb.init(width, height, false);
u8 *p = (u8 *)pb.data(0, 0);
p[0] = info.vo_border_col._blue;
p[1] = info.vo_border_col._green;
p[2] = info.vo_border_col._red;
p[3] = 255;
}
else
{
ReadFramebuffer<BGRAPacker>(info, pb, width, height);
}
if (dcfbTexture)
{
@ -947,12 +935,43 @@ void DX11Renderer::readDCFramebuffer()
}
deviceContext->UpdateSubresource(dcfbTexture, 0, nullptr, pb.data(), width * sizeof(u32), width * sizeof(u32) * height);
deviceContext->OMSetRenderTargets(1, &fbRenderTarget.get(), depthTexView);
float colors[4];
info.vo_border_col.getRGBColor(colors);
colors[3] = 1.f;
deviceContext->ClearRenderTargetView(fbRenderTarget, colors);
D3D11_VIEWPORT vp{};
vp.Width = (FLOAT)this->width;
vp.Height = (FLOAT)this->height;
vp.MinDepth = 0.f;
vp.MaxDepth = 1.f;
deviceContext->RSSetViewports(1, &vp);
deviceContext->OMSetBlendState(blendStates.getState(false), nullptr, 0xffffffff);
float bar = (this->width - this->height * 640.f / 480.f) / 2.f;
quad->draw(dcfbTextureView, samplers->getSampler(true), nullptr, bar / this->width * 2.f - 1.f, -1.f, (this->width - bar * 2.f) / this->width * 2.f, 2.f);
#ifndef LIBRETRO
deviceContext->OMSetRenderTargets(1, &theDX11Context.getRenderTarget().get(), nullptr);
renderFramebuffer();
DrawOSD(false);
theDX11Context.setFrameRendered();
#else
theDX11Context.drawOverlay(this->width, this->height);
ID3D11RenderTargetView *nullView = nullptr;
deviceContext->OMSetRenderTargets(1, &nullView, nullptr);
deviceContext->PSSetShaderResources(0, 1, &fbTextureView.get());
#endif
frameRendered = true;
frameRenderedOnce = true;
}
void DX11Renderer::setBaseScissor()
{
bool wide_screen_on = !pvrrc.isRTT && config::Widescreen && !matrices.IsClipped() && !config::Rotate90;
if (!wide_screen_on && !pvrrc.isRenderFramebuffer)
bool wide_screen_on = !pvrrc.isRTT && config::Widescreen && !matrices.IsClipped()
&& !config::Rotate90 && !config::EmulateFramebuffer;
if (!wide_screen_on)
{
float fWidth;
float fHeight;
@ -1163,6 +1182,104 @@ void DX11Renderer::DrawOSD(bool clear_screen)
#endif
}
void DX11Renderer::writeFramebufferToVRAM()
{
u32 width = (TA_GLOB_TILE_CLIP.tile_x_num + 1) * 32;
u32 height = (TA_GLOB_TILE_CLIP.tile_y_num + 1) * 32;
float xscale = SCALER_CTL.hscale == 1 ? 0.5f : 1.f;
float yscale = 1024.f / SCALER_CTL.vscalefactor;
if (std::abs(yscale - 1.f) < 0.01)
yscale = 1.f;
ComPtr<ID3D11Texture2D> fbTexture = fbTex;
if (xscale != 1.f || yscale != 1.f)
{
u32 scaledW = width * xscale;
u32 scaledH = height * yscale;
if (fbScaledTexture)
{
D3D11_TEXTURE2D_DESC desc;
fbScaledTexture->GetDesc(&desc);
if (desc.Width != scaledW || desc.Height != scaledH)
{
fbScaledTexture.reset();
fbScaledTextureView.reset();
fbScaledRenderTarget.reset();
}
}
if (!fbScaledTexture)
{
createTexAndRenderTarget(fbScaledTexture, fbScaledRenderTarget, scaledW, scaledH);
D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc{};
viewDesc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
viewDesc.Texture2D.MipLevels = 1;
device->CreateShaderResourceView(fbScaledTexture, &viewDesc, &fbScaledTextureView.get());
}
D3D11_VIEWPORT vp{};
vp.Width = (FLOAT)width;
vp.Height = (FLOAT)height;
vp.MinDepth = 0.f;
vp.MaxDepth = 1.f;
deviceContext->RSSetViewports(1, &vp);
deviceContext->OMSetBlendState(blendStates.getState(false), nullptr, 0xffffffff);
quad->draw(fbTextureView, samplers->getSampler(true));
width = scaledW;
height = scaledH;
fbTexture = fbScaledTexture;
}
u32 texAddress = pvrrc.fb_W_SOF1 & VRAM_MASK; // TODO SCALER_CTL.interlace, SCALER_CTL.fieldselect
u32 linestride = pvrrc.fb_W_LINESTRIDE * 8;
D3D11_TEXTURE2D_DESC desc;
fbTexture->GetDesc(&desc);
desc.Usage = D3D11_USAGE_STAGING;
desc.BindFlags = 0;
desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ;
ComPtr<ID3D11Texture2D> stagingTex;
HRESULT hr = device->CreateTexture2D(&desc, nullptr, &stagingTex.get());
if (FAILED(hr))
{
WARN_LOG(RENDERER, "Staging RTT texture creation failed");
return;
}
deviceContext->CopyResource(stagingTex, fbTexture);
PixelBuffer<u32> tmp_buf;
tmp_buf.init(width, height);
u8 *p = (u8 *)tmp_buf.data();
D3D11_MAPPED_SUBRESOURCE mappedSubres;
hr = deviceContext->Map(stagingTex, 0, D3D11_MAP_READ, 0, &mappedSubres);
if (FAILED(hr))
{
WARN_LOG(RENDERER, "Failed to map staging RTT texture");
return;
}
if (width * sizeof(u32) == mappedSubres.RowPitch)
memcpy(p, mappedSubres.pData, width * height * sizeof(u32));
else
{
u8 *src = (u8 *)mappedSubres.pData;
for (u32 y = 0; y < height; y++)
{
memcpy(p, src, width * sizeof(u32));
p += width * sizeof(u32);
src += mappedSubres.RowPitch;
}
}
deviceContext->Unmap(stagingTex, 0);
WriteFramebuffer<2, 1, 0, 3>(width, height, (u8 *)tmp_buf.data(), texAddress, pvrrc.fb_W_CTRL, linestride, pvrrc.fb_X_CLIP, pvrrc.fb_Y_CLIP);
}
Renderer *rend_DirectX11()
{
return new DX11Renderer();

View File

@ -35,10 +35,10 @@
struct DX11Renderer : public Renderer
{
bool Init() override;
void Resize(int w, int h) override;
void Term() override;
bool Process(TA_context* ctx) override;
bool Render() override;
void RenderFramebuffer(const FramebufferInfo& info) override;
bool Present() override
{
@ -83,6 +83,7 @@ protected:
float trilinearAlpha;
};
virtual void resize(int w, int h);
bool ensureBufferSize(ComPtr<ID3D11Buffer>& buffer, D3D11_BIND_FLAG bind, u32& currentSize, u32 minSize);
void createDepthTexAndView(ComPtr<ID3D11Texture2D>& texture, ComPtr<ID3D11DepthStencilView>& view, int width, int height, DXGI_FORMAT format = DXGI_FORMAT_D24_UNORM_S8_UINT, UINT bindFlags = 0);
void createTexAndRenderTarget(ComPtr<ID3D11Texture2D>& texture, ComPtr<ID3D11RenderTargetView>& renderTarget, int width, int height);
@ -91,11 +92,11 @@ protected:
void setupPixelShaderConstants();
void updateFogTexture();
void updatePaletteTexture();
void renderDCFramebuffer();
void readRttRenderTarget(u32 texAddress);
void renderFramebuffer();
void setCullMode(int mode);
virtual void setRTTSize(int width, int height) {}
void writeFramebufferToVRAM();
ComPtr<ID3D11Device> device;
ComPtr<ID3D11DeviceContext> deviceContext;
@ -153,6 +154,9 @@ private:
ComPtr<ID3D11DepthStencilView> rttDepthTexView;
ComPtr<ID3D11Texture2D> whiteTexture;
ComPtr<ID3D11ShaderResourceView> whiteTextureView;
ComPtr<ID3D11Texture2D> fbScaledTexture;
ComPtr<ID3D11ShaderResourceView> fbScaledTextureView;
ComPtr<ID3D11RenderTargetView> fbScaledRenderTarget;
ComPtr<ID3D11RasterizerState> rasterCullNone, rasterCullFront, rasterCullBack;

View File

@ -302,7 +302,6 @@ void DX11Context::handleDeviceLost()
term();
init(true);
rend_init_renderer();
rend_resize_renderer();
}
#endif // !LIBRETRO

View File

@ -131,8 +131,8 @@ struct DX11OITRenderer : public DX11Renderer
}
}
void Resize(int w, int h) override {
DX11Renderer::Resize(w, h);
void resize(int w, int h) override {
DX11Renderer::resize(w, h);
checkMaxSize(w, h);
}
@ -634,6 +634,7 @@ struct DX11OITRenderer : public DX11Renderer
bool Render() override
{
resize(pvrrc.framebufferWidth, pvrrc.framebufferHeight);
if (pixelBufferSize != config::PixelBufferSize)
{
buffers.init(device, deviceContext);
@ -651,27 +652,24 @@ struct DX11OITRenderer : public DX11Renderer
deviceContext->IASetInputLayout(mainInputLayout);
if (!pvrrc.isRenderFramebuffer)
{
n2Helper.resetCache();
uploadGeometryBuffers();
n2Helper.resetCache();
uploadGeometryBuffers();
updateFogTexture();
updatePaletteTexture();
updateFogTexture();
updatePaletteTexture();
setupPixelShaderConstants();
setupPixelShaderConstants();
drawStrips();
}
else
{
renderDCFramebuffer();
}
drawStrips();
if (is_rtt)
{
readRttRenderTarget(pvrrc.fb_W_SOF1 & VRAM_MASK);
}
else if (config::EmulateFramebuffer)
{
writeFramebufferToVRAM();
}
else
{
#ifndef LIBRETRO

View File

@ -164,6 +164,8 @@ void D3DRenderer::preReset()
rttTexture.reset();
dcfbSurface.reset();
dcfbTexture.reset();
fbScaledTexture.reset();
fbScaledSurface.reset();
fogTexture.reset();
paletteTexture.reset();
modVolVtxDecl.reset();
@ -191,7 +193,7 @@ void D3DRenderer::postReset()
u32 h = height;
width = 0;
height = 0;
Resize(w, h);
resize(w, h);
verify(ensureVertexBufferSize(vertexBuffer, vertexBufferSize, 4 * 1024 * 1024));
verify(ensureIndexBufferSize(indexBuffer, indexBufferSize, 120 * 1024 * 4));
verifyWin(device->CreateVertexDeclaration(MainVtxElement, &mainVtxDecl.get()));
@ -233,19 +235,37 @@ BaseTextureCacheData *D3DRenderer::GetTexture(TSP tsp, TCW tcw)
return tf;
}
void D3DRenderer::readDCFramebuffer()
void D3DRenderer::RenderFramebuffer(const FramebufferInfo& info)
{
if (FB_R_SIZE.fb_x_size == 0 || FB_R_SIZE.fb_y_size == 0)
return;
PixelBuffer<u32> pb;
int width;
int height;
ReadFramebuffer<BGRAPacker>(pb, width, height);
if (info.fb_r_ctrl.fb_enable == 0 || info.vo_control.blank_video == 1)
{
// Video output disabled
width = height = 1;
pb.init(width, height, false);
u8 *p = (u8 *)pb.data(0, 0);
p[0] = info.vo_border_col._blue;
p[1] = info.vo_border_col._green;
p[2] = info.vo_border_col._red;
p[3] = 255;
}
else
{
ReadFramebuffer<BGRAPacker>(info, pb, width, height);
}
if (dcfbTexture)
{
D3DSURFACE_DESC desc;
dcfbTexture->GetLevelDesc(0, &desc);
if ((int)desc.Width != width || (int)desc.Height != height)
dcfbTexture.reset();
}
if (!dcfbTexture)
{
// FIXME dimension can change
device->CreateTexture(width, height, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &dcfbTexture.get(), 0);
dcfbTexture->GetSurfaceLevel(0, &dcfbSurface.get());
}
@ -261,14 +281,17 @@ void D3DRenderer::readDCFramebuffer()
memcpy(dst + y * rect.Pitch, pb.data() + y * width, width * sizeof(u32));
}
dcfbTexture->UnlockRect(0);
}
void D3DRenderer::renderDCFramebuffer()
{
device->ColorFill(framebufferSurface, 0, D3DCOLOR_ARGB(255, VO_BORDER_COL._red, VO_BORDER_COL._green, VO_BORDER_COL._blue));
u32 bar = (width - height * 640 / 480) / 2;
RECT rd{ (LONG)bar, 0, (LONG)(width - bar), (LONG)height };
device->ColorFill(framebufferSurface, 0, D3DCOLOR_ARGB(255, info.vo_border_col._red, info.vo_border_col._green, info.vo_border_col._blue));
u32 bar = (this->width - this->height * 640 / 480) / 2;
RECT rd{ (LONG)bar, 0, (LONG)(this->width - bar), (LONG)this->height };
device->StretchRect(dcfbSurface, nullptr, framebufferSurface, &rd, D3DTEXF_LINEAR);
displayFramebuffer();
DrawOSD(false);
frameRendered = true;
frameRenderedOnce = true;
theDXContext.setFrameRendered();
}
bool D3DRenderer::Process(TA_context* ctx)
@ -280,15 +303,7 @@ bool D3DRenderer::Process(TA_context* ctx)
texCache.Clear();
texCache.Cleanup();
if (ctx->rend.isRenderFramebuffer)
{
readDCFramebuffer();
return true;
}
else
{
return ta_parse(ctx);
}
return ta_parse(ctx);
}
inline void D3DRenderer::setTexMode(D3DSAMPLERSTATETYPE state, u32 clamp, u32 mirror)
@ -789,7 +804,8 @@ void D3DRenderer::drawStrips()
void D3DRenderer::setBaseScissor()
{
bool wide_screen_on = !pvrrc.isRTT && config::Widescreen && !matrices.IsClipped() && !config::Rotate90;
bool wide_screen_on = !pvrrc.isRTT && config::Widescreen && !matrices.IsClipped()
&& !config::Rotate90 && !config::EmulateFramebuffer;
if (!wide_screen_on)
{
float fWidth;
@ -939,6 +955,8 @@ bool D3DRenderer::Render()
{
if (resetting)
return false;
resize(pvrrc.framebufferWidth, pvrrc.framebufferHeight);
bool is_rtt = pvrrc.isRTT;
backbuffer.reset();
@ -970,100 +988,93 @@ bool D3DRenderer::Render()
devCache.SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
device->Clear(0, NULL, D3DCLEAR_STENCIL | D3DCLEAR_ZBUFFER, 0, 0.0f, 0);
if (!pvrrc.isRenderFramebuffer)
setFirstProvokingVertex(pvrrc);
// Set clip planes at (-1,0) (1,0) (0,-1) and (0,1).
// Helps avoiding interpolation errors on large triangles.
devCache.SetRenderState(D3DRS_CLIPPLANEENABLE, 15);
float v[4] {};
v[3] = 1.f;
// left
v[0] = 1.f;
device->SetClipPlane(0, v);
// right
v[0] = -1.f;
device->SetClipPlane(1, v);
// top
v[0] = 0.f;
v[1] = 1.f;
device->SetClipPlane(2, v);
// bottom
v[1] = -1.f;
device->SetClipPlane(3, v);
verify(ensureVertexBufferSize(vertexBuffer, vertexBufferSize, pvrrc.verts.bytes()));
void *ptr;
verifyWin(vertexBuffer->Lock(0, pvrrc.verts.bytes(), &ptr, D3DLOCK_DISCARD));
memcpy(ptr, pvrrc.verts.head(), pvrrc.verts.bytes());
vertexBuffer->Unlock();
verify(ensureIndexBufferSize(indexBuffer, indexBufferSize, pvrrc.idx.bytes()));
verifyWin(indexBuffer->Lock(0, pvrrc.idx.bytes(), &ptr, D3DLOCK_DISCARD));
memcpy(ptr, pvrrc.idx.head(), pvrrc.idx.bytes());
indexBuffer->Unlock();
if (config::ModifierVolumes && pvrrc.modtrig.used())
{
setFirstProvokingVertex(pvrrc);
// Set clip planes at (-1,0) (1,0) (0,-1) and (0,1).
// Helps avoiding interpolation errors on large triangles.
devCache.SetRenderState(D3DRS_CLIPPLANEENABLE, 15);
float v[4] {};
v[3] = 1.f;
// left
v[0] = 1.f;
device->SetClipPlane(0, v);
// right
v[0] = -1.f;
device->SetClipPlane(1, v);
// top
v[0] = 0.f;
v[1] = 1.f;
device->SetClipPlane(2, v);
// bottom
v[1] = -1.f;
device->SetClipPlane(3, v);
verify(ensureVertexBufferSize(vertexBuffer, vertexBufferSize, pvrrc.verts.bytes()));
void *ptr;
verifyWin(vertexBuffer->Lock(0, pvrrc.verts.bytes(), &ptr, D3DLOCK_DISCARD));
memcpy(ptr, pvrrc.verts.head(), pvrrc.verts.bytes());
vertexBuffer->Unlock();
verify(ensureIndexBufferSize(indexBuffer, indexBufferSize, pvrrc.idx.bytes()));
verifyWin(indexBuffer->Lock(0, pvrrc.idx.bytes(), &ptr, D3DLOCK_DISCARD));
memcpy(ptr, pvrrc.idx.head(), pvrrc.idx.bytes());
indexBuffer->Unlock();
if (config::ModifierVolumes && pvrrc.modtrig.used())
{
verify(ensureVertexBufferSize(modvolBuffer, modvolBufferSize, pvrrc.modtrig.bytes()));
verifyWin(modvolBuffer->Lock(0, pvrrc.modtrig.bytes(), &ptr, D3DLOCK_DISCARD));
memcpy(ptr, pvrrc.modtrig.head(), pvrrc.modtrig.bytes());
modvolBuffer->Unlock();
}
updateFogTexture();
updatePaletteTexture();
devCache.SetVertexShader(shaders.getVertexShader(true));
// VERT and RAM fog color constants
float ps_FOG_COL_VERT[4];
float ps_FOG_COL_RAM[4];
FOG_COL_VERT.getRGBColor(ps_FOG_COL_VERT);
FOG_COL_RAM.getRGBColor(ps_FOG_COL_RAM);
device->SetPixelShaderConstantF(1, ps_FOG_COL_VERT, 1);
device->SetPixelShaderConstantF(2, ps_FOG_COL_RAM, 1);
// Fog density and scale constants
float fog_den_float = FOG_DENSITY.get() * config::ExtraDepthScale;
float fogDensityAndScale[4]= { fog_den_float, 1.f - FPU_SHAD_SCALE.scale_factor / 256.f, 0, 1 };
device->SetPixelShaderConstantF(3, fogDensityAndScale, 1);
// Color clamping
float color_clamp[4];
pvrrc.fog_clamp_min.getRGBAColor(color_clamp);
device->SetPixelShaderConstantF(6, color_clamp, 1);
pvrrc.fog_clamp_max.getRGBAColor(color_clamp);
device->SetPixelShaderConstantF(7, color_clamp, 1);
devCache.SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
device->SetVertexDeclaration(mainVtxDecl);
device->SetStreamSource(0, vertexBuffer, 0, sizeof(Vertex));
device->SetIndices(indexBuffer);
devCache.SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
devCache.SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
devCache.SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
devCache.SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
devCache.SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
devCache.SetRenderState(D3DRS_CLIPPING, FALSE);
setBaseScissor();
if (!SUCCEEDED(device->BeginScene()))
{
WARN_LOG(RENDERER, "Render: BeginScene failed!");
return false;
}
drawStrips();
device->EndScene();
devCache.SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
verify(ensureVertexBufferSize(modvolBuffer, modvolBufferSize, pvrrc.modtrig.bytes()));
verifyWin(modvolBuffer->Lock(0, pvrrc.modtrig.bytes(), &ptr, D3DLOCK_DISCARD));
memcpy(ptr, pvrrc.modtrig.head(), pvrrc.modtrig.bytes());
modvolBuffer->Unlock();
}
else
updateFogTexture();
updatePaletteTexture();
devCache.SetVertexShader(shaders.getVertexShader(true));
// VERT and RAM fog color constants
float ps_FOG_COL_VERT[4];
float ps_FOG_COL_RAM[4];
FOG_COL_VERT.getRGBColor(ps_FOG_COL_VERT);
FOG_COL_RAM.getRGBColor(ps_FOG_COL_RAM);
device->SetPixelShaderConstantF(1, ps_FOG_COL_VERT, 1);
device->SetPixelShaderConstantF(2, ps_FOG_COL_RAM, 1);
// Fog density and scale constants
float fog_den_float = FOG_DENSITY.get() * config::ExtraDepthScale;
float fogDensityAndScale[4]= { fog_den_float, 1.f - FPU_SHAD_SCALE.scale_factor / 256.f, 0, 1 };
device->SetPixelShaderConstantF(3, fogDensityAndScale, 1);
// Color clamping
float color_clamp[4];
pvrrc.fog_clamp_min.getRGBAColor(color_clamp);
device->SetPixelShaderConstantF(6, color_clamp, 1);
pvrrc.fog_clamp_max.getRGBAColor(color_clamp);
device->SetPixelShaderConstantF(7, color_clamp, 1);
devCache.SetRenderState(D3DRS_ZENABLE, D3DZB_TRUE);
device->SetVertexDeclaration(mainVtxDecl);
device->SetStreamSource(0, vertexBuffer, 0, sizeof(Vertex));
device->SetIndices(indexBuffer);
devCache.SetSamplerState(0, D3DSAMP_MINFILTER, D3DTEXF_LINEAR);
devCache.SetSamplerState(0, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR);
devCache.SetRenderState(D3DRS_ALPHABLENDENABLE, FALSE);
devCache.SetRenderState(D3DRS_ALPHATESTENABLE, FALSE);
devCache.SetRenderState(D3DRS_FILLMODE, D3DFILL_SOLID);
devCache.SetRenderState(D3DRS_CLIPPING, FALSE);
setBaseScissor();
if (!SUCCEEDED(device->BeginScene()))
{
renderDCFramebuffer();
WARN_LOG(RENDERER, "Render: BeginScene failed!");
return false;
}
drawStrips();
device->EndScene();
devCache.SetRenderState(D3DRS_CLIPPLANEENABLE, 0);
verifyWin(device->SetRenderTarget(0, backbuffer));
@ -1071,9 +1082,13 @@ bool D3DRenderer::Render()
{
readRttRenderTarget(texAddress);
}
else if (config::EmulateFramebuffer)
{
writeFramebufferToVRAM();
}
else
{
renderFramebuffer();
displayFramebuffer();
DrawOSD(false);
frameRendered = true;
frameRenderedOnce = true;
@ -1083,7 +1098,7 @@ bool D3DRenderer::Render()
return !is_rtt;
}
void D3DRenderer::Resize(int w, int h)
void D3DRenderer::resize(int w, int h)
{
if (width == (u32)w && height == (u32)h)
return;
@ -1099,7 +1114,7 @@ void D3DRenderer::Resize(int w, int h)
frameRenderedOnce = false;
}
void D3DRenderer::renderFramebuffer()
void D3DRenderer::displayFramebuffer()
{
devCache.SetRenderState(D3DRS_SCISSORTESTENABLE, FALSE);
device->ColorFill(backbuffer, 0, D3DCOLOR_ARGB(255, VO_BORDER_COL._red, VO_BORDER_COL._green, VO_BORDER_COL._blue));
@ -1164,7 +1179,7 @@ bool D3DRenderer::RenderLastFrame()
return false;
backbuffer.reset();
verifyWin(device->GetRenderTarget(0, &backbuffer.get()));
renderFramebuffer();
displayFramebuffer();
return true;
}
@ -1223,6 +1238,75 @@ void D3DRenderer::DrawOSD(bool clear_screen)
theDXContext.setOverlay(false);
}
void D3DRenderer::writeFramebufferToVRAM()
{
u32 width = (TA_GLOB_TILE_CLIP.tile_x_num + 1) * 32;
u32 height = (TA_GLOB_TILE_CLIP.tile_y_num + 1) * 32;
float xscale = SCALER_CTL.hscale == 1 ? 0.5f : 1.f;
float yscale = 1024.f / SCALER_CTL.vscalefactor;
if (std::abs(yscale - 1.f) < 0.01)
yscale = 1.f;
ComPtr<IDirect3DSurface9> fbSurface = framebufferSurface;
if (xscale != 1.f || yscale != 1.f)
{
u32 scaledW = width * xscale;
u32 scaledH = height * yscale;
if (fbScaledTexture)
{
D3DSURFACE_DESC desc;
fbScaledTexture->GetLevelDesc(0, &desc);
if (desc.Width != scaledW || desc.Height != scaledH)
{
fbScaledTexture.reset();
fbScaledSurface.reset();
}
}
if (!fbScaledTexture)
{
device->CreateTexture(scaledW, scaledH, 1, D3DUSAGE_RENDERTARGET, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &fbScaledTexture.get(), NULL);
fbScaledTexture->GetSurfaceLevel(0, &fbScaledSurface.get());
}
device->StretchRect(framebufferSurface, nullptr, fbScaledSurface, nullptr, D3DTEXF_LINEAR);
width = scaledW;
height = scaledH;
fbSurface = fbScaledSurface;
}
u32 texAddress = pvrrc.fb_W_SOF1 & VRAM_MASK; // TODO SCALER_CTL.interlace, SCALER_CTL.fieldselect
u32 linestride = pvrrc.fb_W_LINESTRIDE * 8;
ComPtr<IDirect3DSurface9> offscreenSurface;
verifyWin(device->CreateOffscreenPlainSurface(width, height, D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &offscreenSurface.get(), nullptr));
verifyWin(device->GetRenderTargetData(fbSurface, offscreenSurface));
PixelBuffer<u32> tmp_buf;
tmp_buf.init(width, height);
u8 *p = (u8 *)tmp_buf.data();
D3DLOCKED_RECT rect;
RECT lockRect { 0, 0, (long)width, (long)height };
verifyWin(offscreenSurface->LockRect(&rect, &lockRect, D3DLOCK_READONLY));
if ((u32)rect.Pitch == width * sizeof(u32))
memcpy(p, rect.pBits, width * height * sizeof(u32));
else
{
u8 *src = (u8 *)rect.pBits;
for (u32 y = 0; y < height; y++)
{
memcpy(p, src, width * sizeof(u32));
src += rect.Pitch;
p += width * sizeof(u32);
}
}
verifyWin(offscreenSurface->UnlockRect());
WriteFramebuffer<2, 1, 0, 3>(width, height, (u8 *)tmp_buf.data(), texAddress, pvrrc.fb_W_CTRL, linestride, pvrrc.fb_X_CLIP, pvrrc.fb_Y_CLIP);
}
Renderer* rend_DirectX9()
{
return new D3DRenderer();

View File

@ -100,7 +100,6 @@ public:
struct D3DRenderer : public Renderer
{
bool Init() override;
void Resize(int w, int h) override;
void Term() override;
bool Process(TA_context* ctx) override;
bool Render() override;
@ -117,10 +116,12 @@ struct D3DRenderer : public Renderer
BaseTextureCacheData *GetTexture(TSP tsp, TCW tcw) override;
void preReset();
void postReset();
void RenderFramebuffer(const FramebufferInfo& info) override;
private:
enum ModifierVolumeMode { Xor, Or, Inclusion, Exclusion, ModeCount };
void resize(int w, int h);
void drawStrips();
template <u32 Type, bool SortingEnabled>
void drawList(const List<PolyParam>& gply, int first, int count);
@ -130,9 +131,7 @@ private:
bool ensureIndexBufferSize(ComPtr<IDirect3DIndexBuffer9>& buffer, u32& currentSize, u32 minSize);
void updatePaletteTexture();
void updateFogTexture();
void renderFramebuffer();
void readDCFramebuffer();
void renderDCFramebuffer();
void displayFramebuffer();
void sortTriangles(int first, int count);
void drawSorted(bool multipass);
void setMVS_Mode(ModifierVolumeMode mv_mode, ISP_Modvol ispc);
@ -140,8 +139,8 @@ private:
void setTexMode(D3DSAMPLERSTATETYPE state, u32 clamp, u32 mirror);
void setBaseScissor();
void prepareRttRenderTarget(u32 texAddress);
void readRttRenderTarget(u32 texAddress);
void writeFramebufferToVRAM();
RenderStateCache devCache;
ComPtr<IDirect3DDevice9> device;
@ -166,6 +165,8 @@ private:
ComPtr<IDirect3DTexture9> rttTexture;
ComPtr<IDirect3DSurface9> rttSurface;
ComPtr<IDirect3DSurface9> depthSurface;
ComPtr<IDirect3DTexture9> fbScaledTexture;
ComPtr<IDirect3DSurface9> fbScaledSurface;
u32 width = 0;
u32 height = 0;

View File

@ -120,7 +120,6 @@ void DXContext::Present()
{
renderer = new D3DRenderer();
renderer->Init();
rend_resize_renderer();
}
}
}

View File

@ -773,8 +773,8 @@ void gl4DrawVmuTexture(u8 vmu_screen_number)
const float vmu_padding = 8.f;
const float x_scale = 100.f / config::ScreenStretching;
const float y_scale = (float)gl.ofbo.width / gl.ofbo.height >= 8.f / 3.f - 0.1f ? 0.5f : 1.f;
float x = (config::Widescreen && config::ScreenStretching == 100 ? -1 / gl4ShaderUniforms.ndcMat[0][0] / 4.f : 0) + vmu_padding;
const float y_scale = gl.ofbo.framebuffer && (float)gl.ofbo.framebuffer->getWidth() / gl.ofbo.framebuffer->getHeight() >= 8.f / 3.f - 0.1f ? 0.5f : 1.f;
float x = (config::Widescreen && config::ScreenStretching == 100 && !config::EmulateFramebuffer ? -1 / gl4ShaderUniforms.ndcMat[0][0] / 4.f : 0) + vmu_padding;
float y = vmu_padding;
float w = (float)VMU_SCREEN_WIDTH * vmu_screen_params[vmu_screen_number].vmu_screen_size_mult * x_scale;
float h = (float)VMU_SCREEN_HEIGHT * vmu_screen_params[vmu_screen_number].vmu_screen_size_mult * y_scale;

View File

@ -22,6 +22,7 @@
#include "rend/osd.h"
#include "glsl.h"
#include "gl4naomi2.h"
#include "rend/gles/postprocess.h"
//Fragment and vertex shaders code
@ -733,7 +734,6 @@ static void resize(int w, int h)
}
gl4CreateTextures(max_image_width, max_image_height);
reshapeABuffer(max_image_width, max_image_height);
glBindFramebuffer(GL_FRAMEBUFFER, gl.ofbo.origFbo);
}
}
@ -746,13 +746,7 @@ static bool RenderFrame(int width, int height)
const glm::mat4& scissor_mat = matrices.GetScissorMatrix();
ViewportMatrix = matrices.GetViewportMatrix();
#ifdef LIBRETRO
gl.ofbo.origFbo = glsm_get_current_framebuffer();
#else
gl.ofbo.origFbo = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&gl.ofbo.origFbo);
#endif
if (!is_rtt)
if (!is_rtt && !config::EmulateFramebuffer)
gcflip = 0;
else
gcflip = 1;
@ -807,10 +801,10 @@ static bool RenderFrame(int width, int height)
else
{
#ifdef LIBRETRO
gl.ofbo.width = width;
gl.ofbo.height = height;
if (config::PowerVR2Filter && !pvrrc.isRenderFramebuffer)
if (config::PowerVR2Filter)
output_fbo = postProcessor.getFramebuffer(width, height);
else if (config::EmulateFramebuffer)
output_fbo = init_output_framebuffer(width, height);
else
output_fbo = glsm_get_current_framebuffer();
glViewport(0, 0, width, height);
@ -833,7 +827,7 @@ static bool RenderFrame(int width, int height)
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT);
}
else if (!pvrrc.isRenderFramebuffer)
else
{
//Main VBO
//move vertex to gpu
@ -861,7 +855,7 @@ static bool RenderFrame(int width, int height)
}
glCheck();
if (is_rtt || !config::Widescreen || matrices.IsClipped() || config::Rotate90)
if (is_rtt || !config::Widescreen || matrices.IsClipped() || config::Rotate90 || config::EmulateFramebuffer)
{
float fWidth;
float fHeight;
@ -932,24 +926,24 @@ static bool RenderFrame(int width, int height)
gl4DrawStrips(output_fbo, rendering_width, rendering_height);
#ifdef LIBRETRO
if (config::PowerVR2Filter && !is_rtt)
postProcessor.render(glsm_get_current_framebuffer());
{
if (config::EmulateFramebuffer)
postProcessor.render(init_output_framebuffer(width, height));
else
postProcessor.render(glsm_get_current_framebuffer());
}
#endif
}
else
{
glBindFramebuffer(GL_FRAMEBUFFER, output_fbo);
glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE);
glClear(GL_COLOR_BUFFER_BIT);
DrawFramebuffer();
}
if (is_rtt)
ReadRTTBuffer();
else if (config::EmulateFramebuffer)
writeFramebufferToVRAM();
#ifndef LIBRETRO
else
else {
gl.ofbo.aspectRatio = getOutputFramebufferAspectRatio();
render_output_framebuffer();
}
#endif
glBindVertexArray(0);
@ -963,13 +957,6 @@ struct OpenGL4Renderer : OpenGLRenderer
return gl4_init();
}
void Resize(int w, int h) override
{
width = w;
height = h;
resize(w, h);
}
void Term() override
{
termABuffer();
@ -995,21 +982,24 @@ struct OpenGL4Renderer : OpenGLRenderer
bool Render() override
{
RenderFrame(width, height);
if (pvrrc.isRTT)
saveCurrentFramebuffer();
RenderFrame(pvrrc.framebufferWidth, pvrrc.framebufferHeight);
if (pvrrc.isRTT) {
restoreCurrentFramebuffer();
return false;
}
DrawOSD(false);
frameRendered = true;
if (!config::EmulateFramebuffer)
{
DrawOSD(false);
gl.ofbo2.ready = false;
frameRendered = true;
}
restoreCurrentFramebuffer();
return true;
}
bool RenderLastFrame() override
{
return render_output_framebuffer();
}
GLenum getFogTextureSlot() const override {
return GL_TEXTURE5;
}

View File

@ -698,20 +698,103 @@ void DrawStrips()
}
}
void DrawFramebuffer()
void OpenGLRenderer::RenderFramebuffer(const FramebufferInfo& info)
{
int sx = (int)roundf((gl.ofbo.width - 4.f / 3.f * gl.ofbo.height) / 2.f);
glViewport(sx, 0, gl.ofbo.width - sx * 2, gl.ofbo.height);
drawQuad(fbTextureId, false, true);
glcache.DeleteTextures(1, &fbTextureId);
fbTextureId = 0;
glReadFramebuffer(info);
saveCurrentFramebuffer();
#ifndef LIBRETRO
if (gl.ofbo2.framebuffer != nullptr
&& (gl.dcfb.width != gl.ofbo2.framebuffer->getWidth() || gl.dcfb.height != gl.ofbo2.framebuffer->getHeight()))
gl.ofbo2.framebuffer.reset();
if (gl.ofbo2.framebuffer == nullptr)
gl.ofbo2.framebuffer = std::unique_ptr<GlFramebuffer>(new GlFramebuffer(gl.dcfb.width, gl.dcfb.height));
else
gl.ofbo2.framebuffer->bind();
glCheck();
gl.ofbo2.ready = true;
#endif
gl.ofbo.aspectRatio = getDCFramebufferAspectRatio();
glViewport(0, 0, gl.dcfb.width, gl.dcfb.height);
glcache.Disable(GL_SCISSOR_TEST);
if (info.fb_r_ctrl.fb_enable == 0 || info.vo_control.blank_video == 1)
{
// Video output disabled
glcache.ClearColor(info.vo_border_col.red(), info.vo_border_col.green(), info.vo_border_col.blue(), 1.f);
glClear(GL_COLOR_BUFFER_BIT);
}
else
{
drawQuad(gl.dcfb.tex, false, true);
}
#ifndef LIBRETRO
render_output_framebuffer();
#endif
DrawOSD(false);
frameRendered = true;
restoreCurrentFramebuffer();
}
void writeFramebufferToVRAM()
{
u32 width = (TA_GLOB_TILE_CLIP.tile_x_num + 1) * 32;
u32 height = (TA_GLOB_TILE_CLIP.tile_y_num + 1) * 32;
float xscale = SCALER_CTL.hscale == 1 ? 0.5f : 1.f;
float yscale = 1024.f / SCALER_CTL.vscalefactor;
if (std::abs(yscale - 1.f) < 0.01)
yscale = 1.f;
if (xscale != 1.f || yscale != 1.f)
{
u32 scaledW = width * xscale;
u32 scaledH = height * yscale;
if (gl.fbscaling.framebuffer != nullptr
&& (gl.fbscaling.framebuffer->getWidth() != (int)scaledW || gl.fbscaling.framebuffer->getHeight() != (int)scaledH))
gl.fbscaling.framebuffer.reset();
if (gl.fbscaling.framebuffer == nullptr)
gl.fbscaling.framebuffer = std::unique_ptr<GlFramebuffer>(new GlFramebuffer(scaledW, scaledH));
gl.ofbo.framebuffer->bind(GL_READ_FRAMEBUFFER);
gl.fbscaling.framebuffer->bind(GL_DRAW_FRAMEBUFFER);
glBlitFramebuffer(0, 0, width, height,
0, 0, scaledW, scaledH,
GL_COLOR_BUFFER_BIT, GL_LINEAR);
gl.fbscaling.framebuffer->bind();
width = scaledW;
height = scaledH;
}
u32 tex_addr = pvrrc.fb_W_SOF1 & VRAM_MASK; // TODO SCALER_CTL.interlace, SCALER_CTL.fieldselect
glPixelStorei(GL_PACK_ALIGNMENT, 1);
u32 linestride = pvrrc.fb_W_LINESTRIDE * 8;
PixelBuffer<u32> tmp_buf;
tmp_buf.init(width, height);
u8 *p = (u8 *)tmp_buf.data();
glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE, p);
WriteFramebuffer(width, height, p, tex_addr, pvrrc.fb_W_CTRL, linestride, pvrrc.fb_X_CLIP, pvrrc.fb_Y_CLIP);
glBindFramebuffer(GL_FRAMEBUFFER, gl.ofbo.origFbo);
glCheck();
}
bool render_output_framebuffer()
{
GlFramebuffer *framebuffer = gl.ofbo2.ready ? gl.ofbo2.framebuffer.get() : gl.ofbo.framebuffer.get();
if (framebuffer == nullptr)
return false;
glcache.Disable(GL_SCISSOR_TEST);
float screenAR = (float)settings.display.width / settings.display.height;
float renderAR = getOutputFramebufferAspectRatio();
float renderAR = gl.ofbo.aspectRatio;
int dx = 0;
int dy = 0;
@ -722,26 +805,22 @@ bool render_output_framebuffer()
if (gl.gl_major < 3 || config::Rotate90)
{
if (gl.ofbo.tex == 0)
return false;
glViewport(dx, dy, settings.display.width - dx * 2, settings.display.height - dy * 2);
glBindFramebuffer(GL_FRAMEBUFFER, gl.ofbo.origFbo);
glcache.ClearColor(VO_BORDER_COL.red(), VO_BORDER_COL.green(), VO_BORDER_COL.blue(), 1.f);
glClear(GL_COLOR_BUFFER_BIT);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, config::TextureFiltering == 1 ? GL_NEAREST : GL_LINEAR);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, config::TextureFiltering == 1 ? GL_NEAREST : GL_LINEAR);
drawQuad(gl.ofbo.tex, config::Rotate90);
drawQuad(framebuffer->getTexture(), config::Rotate90);
}
else
{
#ifndef GLES2
if (gl.ofbo.fbo == 0)
return false;
glBindFramebuffer(GL_READ_FRAMEBUFFER, gl.ofbo.fbo);
framebuffer->bind(GL_READ_FRAMEBUFFER);
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, gl.ofbo.origFbo);
glcache.ClearColor(VO_BORDER_COL.red(), VO_BORDER_COL.green(), VO_BORDER_COL.blue(), 1.f);
glClear(GL_COLOR_BUFFER_BIT);
glBlitFramebuffer(0, 0, gl.ofbo.width, gl.ofbo.height,
glBlitFramebuffer(0, 0, framebuffer->getWidth(), framebuffer->getHeight(),
dx, dy, settings.display.width - dx, settings.display.height - dy,
GL_COLOR_BUFFER_BIT, config::TextureFiltering == 1 ? GL_NEAREST : GL_LINEAR);
glBindFramebuffer(GL_FRAMEBUFFER, gl.ofbo.origFbo);
@ -822,8 +901,8 @@ void DrawVmuTexture(u8 vmu_screen_number)
const float vmu_padding = 8.f;
const float x_scale = 100.f / config::ScreenStretching;
const float y_scale = (float)gl.ofbo.width / gl.ofbo.height >= 8.f / 3.f - 0.1f ? 0.5f : 1.f;
float x = (config::Widescreen && config::ScreenStretching == 100 ? -1 / ShaderUniforms.ndcMat[0][0] / 4.f : 0) + vmu_padding;
const float y_scale = gl.ofbo.framebuffer && (float)gl.ofbo.framebuffer->getWidth() / gl.ofbo.framebuffer->getHeight() >= 8.f / 3.f - 0.1f ? 0.5f : 1.f;
float x = (config::Widescreen && config::ScreenStretching == 100 && !config::EmulateFramebuffer ? -1 / ShaderUniforms.ndcMat[0][0] / 4.f : 0) + vmu_padding;
float y = vmu_padding;
float w = (float)VMU_SCREEN_WIDTH * vmu_screen_params[vmu_screen_number].vmu_screen_size_mult * x_scale;
float h = (float)VMU_SCREEN_HEIGHT * vmu_screen_params[vmu_screen_number].vmu_screen_size_mult * y_scale;

View File

@ -13,6 +13,7 @@
#include "wsi/gl_context.h"
#include "emulator.h"
#include "naomi2.h"
#include "rend/gles/postprocess.h"
#include <cmath>
@ -356,6 +357,8 @@ void main()
}
)";
static void gl_free_osd_resources();
GLCache glcache;
gl_ctx gl;
@ -428,18 +431,15 @@ void termGLCommon()
glDeleteBuffers(1, &gl.rtt.pbo);
gl.rtt.pbo = 0;
gl.rtt.pboSize = 0;
glDeleteFramebuffers(1, &gl.rtt.fbo);
gl.rtt.fbo = 0;
glcache.DeleteTextures(1, &gl.rtt.tex);
gl.rtt.tex = 0;
glDeleteRenderbuffers(1, &gl.rtt.depthb);
gl.rtt.depthb = 0;
gl.rtt.framebuffer.reset();
gl.rtt.texAddress = ~0;
gl_free_osd_resources();
free_output_framebuffer();
glcache.DeleteTextures(1, &fbTextureId);
fbTextureId = 0;
gl.ofbo.framebuffer.reset();
glcache.DeleteTextures(1, &gl.dcfb.tex);
gl.dcfb.tex = 0;
gl.ofbo2.framebuffer.reset();
gl.fbscaling.framebuffer.reset();
#ifdef LIBRETRO
termVmuLightgun();
#endif
@ -799,6 +799,7 @@ bool CompilePipelineShader(PipelineShader* s)
return glIsProgram(s->program)==GL_TRUE;
}
#ifdef __ANDROID__
static void SetupOSDVBO()
{
#ifndef GLES2
@ -828,7 +829,7 @@ static void SetupOSDVBO()
bindVertexArray(0);
}
void gl_load_osd_resources()
static void gl_load_osd_resources()
{
OpenGlSource vertexSource;
vertexSource.addSource(VertexCompatShader)
@ -841,7 +842,6 @@ void gl_load_osd_resources()
gl.OSD_SHADER.scale = glGetUniformLocation(gl.OSD_SHADER.program, "scale");
glUniform1i(glGetUniformLocation(gl.OSD_SHADER.program, "tex"), 0); //bind osd texture to slot 0
#ifdef __ANDROID__
if (gl.OSD_SHADER.osd_tex == 0)
{
int width, height;
@ -854,11 +854,11 @@ void gl_load_osd_resources()
delete[] image_data;
}
#endif
SetupOSDVBO();
}
#endif
void gl_free_osd_resources()
static void gl_free_osd_resources()
{
if (gl.OSD_SHADER.program != 0)
{
@ -1001,7 +1001,7 @@ bool gles_init()
}
void UpdateFogTexture(u8 *fog_table, GLenum texture_slot, GLint fog_image_format)
static void updateFogTexture(u8 *fog_table, GLenum texture_slot, GLint fog_image_format)
{
glActiveTexture(texture_slot);
if (fogTextureId == 0)
@ -1026,7 +1026,7 @@ void UpdateFogTexture(u8 *fog_table, GLenum texture_slot, GLint fog_image_format
glActiveTexture(GL_TEXTURE0);
}
void UpdatePaletteTexture(GLenum texture_slot)
static void updatePaletteTexture(GLenum texture_slot)
{
glActiveTexture(texture_slot);
if (paletteTextureId == 0)
@ -1139,25 +1139,17 @@ bool OpenGLRenderer::Process(TA_context* ctx)
TexCache.Clear();
TexCache.Cleanup();
if (ctx->rend.isRenderFramebuffer)
if (fog_needs_update && config::Fog)
{
RenderFramebuffer();
return true;
fog_needs_update = false;
updateFogTexture((u8 *)FOG_TABLE, getFogTextureSlot(), gl.single_channel_format);
}
else
if (palette_updated)
{
if (fog_needs_update && config::Fog)
{
fog_needs_update = false;
UpdateFogTexture((u8 *)FOG_TABLE, getFogTextureSlot(), gl.single_channel_format);
}
if (palette_updated)
{
UpdatePaletteTexture(getPaletteTextureSlot());
palette_updated = false;
}
return ta_parse(ctx);
updatePaletteTexture(getPaletteTextureSlot());
palette_updated = false;
}
return ta_parse(ctx);
}
static void upload_vertex_indices()
@ -1199,7 +1191,7 @@ bool RenderFrame(int width, int height)
const glm::mat4& scissor_mat = matrices.GetScissorMatrix();
ViewportMatrix = matrices.GetViewportMatrix();
if (!is_rtt)
if (!is_rtt && !config::EmulateFramebuffer)
gcflip = 0;
else
gcflip = 1;
@ -1245,12 +1237,6 @@ bool RenderFrame(int width, int height)
}
//setup render target first
#ifdef LIBRETRO
gl.ofbo.origFbo = glsm_get_current_framebuffer();
#else
gl.ofbo.origFbo = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&gl.ofbo.origFbo);
#endif
if (is_rtt)
{
if (BindRTT() == 0)
@ -1259,12 +1245,17 @@ bool RenderFrame(int width, int height)
else
{
#ifdef LIBRETRO
gl.ofbo.width = width;
gl.ofbo.height = height;
if (config::PowerVR2Filter && !pvrrc.isRenderFramebuffer)
if (config::PowerVR2Filter)
glBindFramebuffer(GL_FRAMEBUFFER, postProcessor.getFramebuffer(width, height));
else if (config::EmulateFramebuffer)
{
if (init_output_framebuffer(width, height) == 0)
return false;
}
else
{
glBindFramebuffer(GL_FRAMEBUFFER, glsm_get_current_framebuffer());
}
glViewport(0, 0, width, height);
#else
if (init_output_framebuffer(width, height) == 0)
@ -1272,7 +1263,8 @@ bool RenderFrame(int width, int height)
#endif
}
bool wide_screen_on = !is_rtt && config::Widescreen && !matrices.IsClipped() && !config::Rotate90;
bool wide_screen_on = !is_rtt && config::Widescreen && !matrices.IsClipped()
&& !config::Rotate90 && !config::EmulateFramebuffer;
//Color is cleared by the background plane
@ -1291,7 +1283,7 @@ bool RenderFrame(int width, int height)
// Video output disabled
glClear(GL_COLOR_BUFFER_BIT);
}
else if (!pvrrc.isRenderFramebuffer)
else
{
//move vertex to gpu
//Main VBO
@ -1373,20 +1365,24 @@ bool RenderFrame(int width, int height)
DrawStrips();
#ifdef LIBRETRO
if (config::PowerVR2Filter && !is_rtt)
postProcessor.render(glsm_get_current_framebuffer());
{
if (config::EmulateFramebuffer)
postProcessor.render(init_output_framebuffer(width, height));
else
postProcessor.render(glsm_get_current_framebuffer());
}
#endif
}
else
{
glClear(GL_COLOR_BUFFER_BIT);
DrawFramebuffer();
}
if (is_rtt)
ReadRTTBuffer();
else if (config::EmulateFramebuffer)
writeFramebufferToVRAM();
#ifndef LIBRETRO
else
else {
gl.ofbo.aspectRatio = getOutputFramebufferAspectRatio();
render_output_framebuffer();
}
#endif
bindVertexArray(0);
@ -1406,12 +1402,20 @@ void OpenGLRenderer::Term()
bool OpenGLRenderer::Render()
{
RenderFrame(width, height);
if (pvrrc.isRTT)
saveCurrentFramebuffer();
RenderFrame(pvrrc.framebufferWidth, pvrrc.framebufferHeight);
if (pvrrc.isRTT) {
restoreCurrentFramebuffer();
return false;
}
DrawOSD(false);
frameRendered = true;
if (!config::EmulateFramebuffer)
{
DrawOSD(false);
frameRendered = true;
gl.ofbo2.ready = false;
}
restoreCurrentFramebuffer();
return true;
}

View File

@ -5,7 +5,6 @@
#include "rend/TexCache.h"
#include "wsi/gl_context.h"
#include "glcache.h"
#include "postprocess.h"
#include "rend/shader_util.h"
#ifndef LIBRETRO
#include "rend/imgui_driver.h"
@ -154,6 +153,36 @@ private:
GLuint name;
};
class GlFramebuffer
{
public:
GlFramebuffer(int width, int height, bool withDepth = false, GLuint texture = 0);
~GlFramebuffer();
void bind(GLenum type = GL_FRAMEBUFFER) const {
glBindFramebuffer(type, framebuffer);
}
int getWidth() const { return width; }
int getHeight() const { return height; }
GLuint getTexture() const { return texture; }
GLuint detachTexture() {
GLuint t = texture;
texture = 0;
return t;
}
GLuint getFramebuffer() const { return framebuffer; }
private:
int width;
int height;
GLuint texture;
GLuint framebuffer = 0;
GLuint colorBuffer = 0;
GLuint depthBuffer = 0;
};
struct gl_ctx
{
struct
@ -201,9 +230,6 @@ struct gl_ctx
struct
{
u32 texAddress = ~0;
GLuint depthb;
GLuint tex;
GLuint fbo;
GLuint pbo;
u32 pboSize;
bool directXfer;
@ -211,19 +237,34 @@ struct gl_ctx
u32 height;
FB_W_CTRL_type fb_w_ctrl;
u32 linestride;
std::unique_ptr<GlFramebuffer> framebuffer;
} rtt;
struct
{
GLuint depthb;
GLuint colorb;
GLuint tex;
GLuint fbo;
int width;
int height;
std::unique_ptr<GlFramebuffer> framebuffer;
float aspectRatio;
GLuint origFbo;
} ofbo;
struct
{
GLuint tex;
int width;
int height;
} dcfb;
struct
{
std::unique_ptr<GlFramebuffer> framebuffer;
} fbscaling;
struct
{
std::unique_ptr<GlFramebuffer> framebuffer;
bool ready = false;
} ofbo2;
const char *gl_version;
const char *glsl_version_header;
int gl_major;
@ -242,36 +283,23 @@ struct gl_ctx
};
extern gl_ctx gl;
extern GLuint fbTextureId;
BaseTextureCacheData *gl_GetTexture(TSP tsp, TCW tcw);
enum ModifierVolumeMode { Xor, Or, Inclusion, Exclusion, ModeCount };
void gl_load_osd_resources();
void gl_free_osd_resources();
bool ProcessFrame(TA_context* ctx);
void UpdateFogTexture(u8 *fog_table, GLenum texture_slot, GLint fog_image_format);
void UpdatePaletteTexture(GLenum texture_slot);
void termGLCommon();
void findGLVersion();
void GetFramebufferScaling(float& scale_x, float& scale_y, float& scissoring_scale_x, float& scissoring_scale_y);
void GetFramebufferSize(float& dc_width, float& dc_height);
void SetupMatrices(float dc_width, float dc_height,
float scale_x, float scale_y, float scissoring_scale_x, float scissoring_scale_y,
float &ds2s_offs_x, glm::mat4& ndcMat, glm::mat4& scissor_mat);
void SetCull(u32 CullMode);
s32 SetTileClip(u32 val, GLint uniform);
void SetMVS_Mode(ModifierVolumeMode mv_mode, ISP_Modvol ispc);
GLuint BindRTT(bool withDepthBuffer = true);
void ReadRTTBuffer();
void RenderFramebuffer();
void DrawFramebuffer();
void glReadFramebuffer(const FramebufferInfo& info);
GLuint init_output_framebuffer(int width, int height);
bool render_output_framebuffer();
void free_output_framebuffer();
void writeFramebufferToVRAM();
void OSD_DRAW(bool clear_screen);
PipelineShader *GetProgram(bool cp_AlphaTest, bool pp_InsideClipping,
@ -375,13 +403,14 @@ extern const u32 SrcBlendGL[], DstBlendGL[];
struct OpenGLRenderer : Renderer
{
bool Init() override;
void Resize(int w, int h) override { width = w; height = h; }
void Term() override;
bool Process(TA_context* ctx) override;
bool Render() override;
void RenderFramebuffer(const FramebufferInfo& info) override;
bool RenderLastFrame() override;
void DrawOSD(bool clear_screen) override { OSD_DRAW(clear_screen); }
@ -409,9 +438,19 @@ struct OpenGLRenderer : Renderer
return GL_TEXTURE2;
}
void saveCurrentFramebuffer() {
#ifdef LIBRETRO
gl.ofbo.origFbo = glsm_get_current_framebuffer();
#else
gl.ofbo.origFbo = 0;
glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&gl.ofbo.origFbo);
#endif
}
void restoreCurrentFramebuffer() {
glBindFramebuffer(GL_FRAMEBUFFER, gl.ofbo.origFbo);
}
bool frameRendered = false;
int width;
int height;
};
void initQuad();

View File

@ -165,12 +165,7 @@ GLuint BindRTT(bool withDepthBuffer)
readAsyncPixelBuffer(gl.rtt.texAddress);
gl.rtt.texAddress = texAddress;
if (gl.rtt.fbo != 0)
glDeleteFramebuffers(1, &gl.rtt.fbo);
if (gl.rtt.tex != 0)
glcache.DeleteTextures(1, &gl.rtt.tex);
if (gl.rtt.depthb != 0)
glDeleteRenderbuffers(1, &gl.rtt.depthb);
gl.rtt.framebuffer.reset();
u32 fbw2;
u32 fbh2;
@ -193,64 +188,15 @@ GLuint BindRTT(bool withDepthBuffer)
#endif
// Create a texture for rendering to
gl.rtt.tex = glcache.GenTexture();
glcache.BindTexture(GL_TEXTURE_2D, gl.rtt.tex);
GLuint texture = glcache.GenTexture();
glcache.BindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, channels, fbw2, fbh2, 0, channels, format, 0);
// Create the object that will allow us to render to the aforementioned texture
glGenFramebuffers(1, &gl.rtt.fbo);
glBindFramebuffer(GL_FRAMEBUFFER, gl.rtt.fbo);
// Attach the texture to the FBO
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl.rtt.tex, 0);
if (withDepthBuffer)
{
// Generate and bind a render buffer which will become a depth buffer
glGenRenderbuffers(1, &gl.rtt.depthb);
glBindRenderbuffer(GL_RENDERBUFFER, gl.rtt.depthb);
// Currently it is unknown to GL that we want our new render buffer to be a depth buffer.
// glRenderbufferStorage will fix this and will allocate a depth buffer
if (gl.is_gles)
{
#if defined(GL_DEPTH24_STENCIL8)
if (gl.gl_major >= 3)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, fbw2, fbh2);
else
#endif
#if defined(GL_DEPTH24_STENCIL8_OES)
if (gl.GL_OES_packed_depth_stencil_supported)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, fbw2, fbh2);
else
#endif
#if defined(GL_DEPTH_COMPONENT24_OES)
if (gl.GL_OES_depth24_supported)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, fbw2, fbh2);
else
#endif
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, fbw2, fbh2);
}
#ifdef GL_DEPTH24_STENCIL8
else
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, fbw2, fbh2);
#endif
// Attach the depth buffer we just created to our FBO.
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, gl.rtt.depthb);
if (!gl.is_gles || gl.gl_major >= 3 || gl.GL_OES_packed_depth_stencil_supported)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gl.rtt.depthb);
}
// Check that our FBO creation was successful
GLuint uStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
verify(uStatus == GL_FRAMEBUFFER_COMPLETE);
gl.rtt.framebuffer = std::unique_ptr<GlFramebuffer>(new GlFramebuffer((int)fbw2, (int)fbh2, withDepthBuffer, texture));
glViewport(0, 0, fbw, fbh);
return gl.rtt.fbo;
return gl.rtt.framebuffer->getFramebuffer();
}
void ReadRTTBuffer()
@ -329,8 +275,7 @@ void ReadRTTBuffer()
{
TextureCacheData *texture_data = TexCache.getRTTexture(gl.rtt.texAddress, fb_packmode, w, h);
glcache.DeleteTextures(1, &texture_data->texID);
texture_data->texID = gl.rtt.tex;
gl.rtt.tex = 0;
texture_data->texID = gl.rtt.framebuffer->detachTexture();
texture_data->dirty = 0;
texture_data->unprotectVRam();
}
@ -409,22 +354,15 @@ BaseTextureCacheData *gl_GetTexture(TSP tsp, TCW tcw)
return tf;
}
GLuint fbTextureId;
void RenderFramebuffer()
void glReadFramebuffer(const FramebufferInfo& info)
{
if (FB_R_SIZE.fb_x_size == 0 || FB_R_SIZE.fb_y_size == 0)
return;
PixelBuffer<u32> pb;
int width;
int height;
ReadFramebuffer(pb, width, height);
ReadFramebuffer(info, pb, gl.dcfb.width, gl.dcfb.height);
if (fbTextureId == 0)
fbTextureId = glcache.GenTexture();
if (gl.dcfb.tex == 0)
gl.dcfb.tex = glcache.GenTexture();
glcache.BindTexture(GL_TEXTURE_2D, fbTextureId);
glcache.BindTexture(GL_TEXTURE_2D, gl.dcfb.tex);
//set texture repeat mode
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
@ -432,55 +370,59 @@ void RenderFramebuffer()
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pb.data());
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, gl.dcfb.width, gl.dcfb.height, 0, GL_RGBA, GL_UNSIGNED_BYTE, pb.data());
}
GLuint init_output_framebuffer(int width, int height)
{
if (width != gl.ofbo.width || height != gl.ofbo.height
// if the rotate90 setting has changed
|| (gl.gl_major >= 3 && (gl.ofbo.tex == 0) == config::Rotate90))
if (gl.ofbo.framebuffer != nullptr
&& (width != gl.ofbo.framebuffer->getWidth() || height != gl.ofbo.framebuffer->getHeight()
// if the rotate90 setting has changed
|| (gl.gl_major >= 3 && (gl.ofbo.framebuffer->getTexture() == 0) == config::Rotate90)))
{
free_output_framebuffer();
gl.ofbo.width = width;
gl.ofbo.height = height;
gl.ofbo.framebuffer.reset();
}
if (gl.ofbo.fbo == 0)
if (gl.ofbo.framebuffer == nullptr)
{
// Create the depth+stencil renderbuffer
glGenRenderbuffers(1, &gl.ofbo.depthb);
glBindRenderbuffer(GL_RENDERBUFFER, gl.ofbo.depthb);
if (gl.is_gles)
{
#if defined(GL_DEPTH24_STENCIL8)
if (gl.gl_major >= 3)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
else
#endif
#if defined(GL_DEPTH24_STENCIL8_OES)
if (gl.GL_OES_packed_depth_stencil_supported)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, width, height);
else
#endif
#if defined(GL_DEPTH_COMPONENT24_OES)
if (gl.GL_OES_depth24_supported)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, width, height);
else
#endif
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
}
#ifdef GL_DEPTH24_STENCIL8
else
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
#endif
if (gl.gl_major < 3 || config::Rotate90)
GLuint texture = 0;
if (config::Rotate90)
{
// Create a texture for rendering to
gl.ofbo.tex = glcache.GenTexture();
glcache.BindTexture(GL_TEXTURE_2D, gl.ofbo.tex);
texture = glcache.GenTexture();
glcache.BindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
gl.ofbo.framebuffer = std::unique_ptr<GlFramebuffer>(new GlFramebuffer(width, height, true, texture));
glcache.Disable(GL_SCISSOR_TEST);
glcache.ClearColor(0.f, 0.f, 0.f, 0.f);
glClear(GL_COLOR_BUFFER_BIT);
}
else
gl.ofbo.framebuffer->bind();
glViewport(0, 0, width, height);
glCheck();
return gl.ofbo.framebuffer->getFramebuffer();
}
GlFramebuffer::GlFramebuffer(int width, int height, bool withDepth, GLuint texture)
: width(width), height(height), texture(texture)
{
if (texture == 0)
{
if (gl.gl_major < 3)
{
// Create a texture for rendering to
texture = glcache.GenTexture();
glcache.BindTexture(GL_TEXTURE_2D, texture);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
@ -491,65 +433,68 @@ GLuint init_output_framebuffer(int width, int height)
else
{
// Use a renderbuffer and glBlitFramebuffer
glGenRenderbuffers(1, &gl.ofbo.colorb);
glBindRenderbuffer(GL_RENDERBUFFER, gl.ofbo.colorb);
glGenRenderbuffers(1, &colorBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, colorBuffer);
#ifdef GL_RGBA8
glRenderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, width, height);
#endif
}
}
// Create the framebuffer
glGenFramebuffers(1, &gl.ofbo.fbo);
glBindFramebuffer(GL_FRAMEBUFFER, gl.ofbo.fbo);
// Create the framebuffer
glGenFramebuffers(1, &framebuffer);
bind();
// Attach the depth buffer to our FBO.
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, gl.ofbo.depthb);
if (withDepth)
{
// Generate and bind a render buffer which will become a depth buffer
glGenRenderbuffers(1, &depthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
// Currently it is unknown to GL that we want our new render buffer to be a depth buffer.
// glRenderbufferStorage will fix this and will allocate a depth buffer
if (gl.is_gles)
{
#if defined(GL_DEPTH24_STENCIL8)
if (gl.gl_major >= 3)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
else
#endif
#if defined(GL_DEPTH24_STENCIL8_OES)
if (gl.GL_OES_packed_depth_stencil_supported)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, width, height);
else
#endif
#if defined(GL_DEPTH_COMPONENT24_OES)
if (gl.GL_OES_depth24_supported)
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT24_OES, width, height);
else
#endif
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height);
}
#ifdef GL_DEPTH24_STENCIL8
else
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
#endif
// Attach the depth buffer we just created to our FBO.
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);
if (!gl.is_gles || gl.gl_major >= 3 || gl.GL_OES_packed_depth_stencil_supported)
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, gl.ofbo.depthb);
// Attach the texture/renderbuffer to the FBO
if (gl.ofbo.tex != 0)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, gl.ofbo.tex, 0);
else
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, gl.ofbo.colorb);
// Check that our FBO creation was successful
GLuint uStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
if (uStatus != GL_FRAMEBUFFER_COMPLETE)
return 0;
glcache.Disable(GL_SCISSOR_TEST);
glcache.ClearColor(0.f, 0.f, 0.f, 0.f);
glClear(GL_COLOR_BUFFER_BIT);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);
}
// Attach the texture/renderbuffer to the FBO
if (texture != 0)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
else
glBindFramebuffer(GL_FRAMEBUFFER, gl.ofbo.fbo);
glViewport(0, 0, width, height);
glCheck();
return gl.ofbo.fbo;
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, colorBuffer);
}
void free_output_framebuffer()
GlFramebuffer::~GlFramebuffer()
{
if (gl.ofbo.fbo != 0)
{
glDeleteFramebuffers(1, &gl.ofbo.fbo);
gl.ofbo.fbo = 0;
glDeleteRenderbuffers(1, &gl.ofbo.depthb);
gl.ofbo.depthb = 0;
if (gl.ofbo.tex != 0)
{
glcache.DeleteTextures(1, &gl.ofbo.tex);
gl.ofbo.tex = 0;
}
if (gl.ofbo.colorb != 0)
{
glDeleteRenderbuffers(1, &gl.ofbo.colorb);
gl.ofbo.colorb = 0;
}
}
glDeleteFramebuffers(1, &framebuffer);
glDeleteRenderbuffers(1, &depthBuffer);
glcache.DeleteTextures(1, &texture);
glDeleteRenderbuffers(1, &colorBuffer);
}

View File

@ -50,8 +50,8 @@ OpenGLDriver::OpenGLDriver()
for (auto& tex : vmu_lcd_tex_ids)
tex = ImTextureID();
ImGui_ImplOpenGL3_Init();
EventManager::listen(Event::Resume, emuEventCallback, this);
EventManager::listen(Event::Pause, emuEventCallback, this);
EventManager::listen(Event::Start, emuEventCallback, this);
EventManager::listen(Event::Terminate, emuEventCallback, this);
}
OpenGLDriver::~OpenGLDriver()

View File

@ -53,10 +53,10 @@ private:
{
switch (event)
{
case Event::Resume:
case Event::Start:
gameStarted = true;
break;
case Event::Pause:
case Event::Terminate:
gameStarted = false;
break;
default:

View File

@ -217,32 +217,9 @@ private:
std::array<PostProcessShader, 8> PostProcessShader::shaders;
void PostProcessor::init()
void PostProcessor::init(int width, int height)
{
glGenFramebuffers(1, &framebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, framebuffer);
texture = glcache.GenTexture();
glcache.BindTexture(GL_TEXTURE_2D, texture);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, NULL);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, texture, 0);
glGenRenderbuffers(1, &depthBuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthBuffer);
#ifdef GLES2
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);
#else
glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthBuffer);
#endif
GLuint uStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
verify(uStatus == GL_FRAMEBUFFER_COMPLETE);
glcache.BindTexture(GL_TEXTURE_2D, 0);
framebuffer = std::unique_ptr<GlFramebuffer>(new GlFramebuffer(width, height));
float vertices[] = {
-1, 1, 1,
@ -271,12 +248,7 @@ void PostProcessor::init()
void PostProcessor::term()
{
glcache.DeleteTextures(1, &texture);
texture = 0;
glDeleteFramebuffers(1, &framebuffer);
framebuffer = 0;
glDeleteRenderbuffers(1, &depthBuffer);
depthBuffer = 0;
framebuffer.reset();
glDeleteBuffers(1, &vertexBuffer);
vertexBuffer = 0;
deleteVertexArray(vertexArray);
@ -288,15 +260,14 @@ void PostProcessor::term()
GLuint PostProcessor::getFramebuffer(int width, int height)
{
if (width != this->width || height != this->height)
if (framebuffer != nullptr
&& (width != framebuffer->getWidth() || height != framebuffer->getHeight()))
term();
if (framebuffer == 0) {
this->width = width;
this->height = height;
init();
}
return framebuffer;
if (framebuffer == nullptr)
init(width, height);
return framebuffer->getFramebuffer();
}
void PostProcessor::render(GLuint output_fbo)
@ -322,7 +293,7 @@ void PostProcessor::render(GLuint output_fbo)
glBindFramebuffer(GL_FRAMEBUFFER, output_fbo);
glActiveTexture(GL_TEXTURE0);
glcache.BindTexture(GL_TEXTURE_2D, texture);
glcache.BindTexture(GL_TEXTURE_2D, framebuffer->getTexture());
glcache.ClearColor(0.f, 0.f, 0.f, 0.f);
glClear(GL_COLOR_BUFFER_BIT);

View File

@ -27,15 +27,11 @@ public:
GLuint getFramebuffer(int width, int height);
private:
void init();
void init(int width, int height);
GLuint texture = 0;
GLuint framebuffer = 0;
GLuint depthBuffer = 0;
GLuint vertexBuffer = 0;
GLuint vertexArray = 0;
float width = 0;
float height = 0;
std::unique_ptr<GlFramebuffer> framebuffer;
};
extern PostProcessor postProcessor;

View File

@ -1771,6 +1771,9 @@ static void gui_display_settings()
"Useful to avoid flashing screen or glitchy videos. Not recommended on slow platforms");
OptionCheckbox("Native Depth Interpolation", config::NativeDepthInterpolation,
"Helps with texture corruption and depth issues on AMD GPUs. Can also help Intel GPUs in some cases.");
OptionCheckbox("Full Framebuffer Emulation", config::EmulateFramebuffer,
"Fully accurate VRAM framebuffer emulation. Helps games that directly access the framebuffer for special effects. "
"Very slow and incompatible with upscaling and wide screen.");
constexpr int apiCount = 0
#ifdef USE_VULKAN
+ 1

View File

@ -67,7 +67,6 @@ bool mainui_rend_frame()
void mainui_init()
{
rend_init_renderer();
rend_resize_renderer();
}
void mainui_term()

View File

@ -26,6 +26,29 @@
#include <glm/glm.hpp>
#include <glm/gtx/transform.hpp>
inline static void getTAViewport(int& width, int& height)
{
width = (TA_GLOB_TILE_CLIP.tile_x_num + 1) * 32;
height = (TA_GLOB_TILE_CLIP.tile_y_num + 1) * 32;
}
inline static void getPvrFramebufferSize(int& width, int& height)
{
getTAViewport(width, height);
if (!config::EmulateFramebuffer)
{
int maxHeight = FB_R_CTRL.vclk_div == 0 && SPG_CONTROL.interlace == 0 ? 240 : 480;
if (SCALER_CTL.vscalefactor != 0
&& (SCALER_CTL.vscalefactor > 1025 || SCALER_CTL.vscalefactor < 1024)
&& SPG_CONTROL.interlace == 0)
maxHeight /= 1024.f / SCALER_CTL.vscalefactor;
if (FB_R_CTRL.fb_line_double)
maxHeight /= 2;
height = std::min(maxHeight, height);
// TODO Use FB_R_SIZE too?
}
}
// Dreamcast:
// +Y is down
// OpenGL:
@ -48,10 +71,12 @@ public:
bool IsClipped() const
{
int width, height;
getTAViewport(width, height);
float sx, sy;
GetScissorScaling(sx, sy);
return renderingContext->fb_X_CLIP.min != 0
|| lroundf((renderingContext->fb_X_CLIP.max + 1) / scale_x) != 640L
|| renderingContext->fb_Y_CLIP.min != 0
|| lroundf((renderingContext->fb_Y_CLIP.max + 1) / scale_y) != 480L;
|| lroundf((renderingContext->fb_X_CLIP.max + 1) / sx) != width;
}
const glm::mat4& GetNormalMatrix() const {
@ -76,15 +101,13 @@ public:
void CalcMatrices(const rend_context *renderingContext, int width = 0, int height = 0)
{
constexpr int screenFlipY = System == COORD_OPENGL || System == COORD_DIRECTX ? -1 : 1;
const int screenFlipY = (System == COORD_OPENGL && !config::EmulateFramebuffer) || System == COORD_DIRECTX ? -1 : 1;
constexpr int rttFlipY = System == COORD_DIRECTX ? -1 : 1;
constexpr int framebufferFlipY = System == COORD_DIRECTX ? -1 : 1;
renderViewport = { width == 0 ? settings.display.width : width, height == 0 ? settings.display.height : height };
this->renderingContext = renderingContext;
GetFramebufferScaling(false, scale_x, scale_y);
if (renderingContext->isRTT)
{
dcViewport.x = (float)(renderingContext->fb_X_CLIP.max - renderingContext->fb_X_CLIP.min + 1);
@ -96,8 +119,10 @@ public:
}
else
{
dcViewport.x = 640.f * scale_x;
dcViewport.y = 480.f * scale_y;
int w, h;
getPvrFramebufferSize(w, h);
dcViewport.x = w;
dcViewport.y = h;
float startx = 0;
float starty = 0;
@ -142,9 +167,9 @@ public:
scissorMatrix = normalMatrix;
float scissoring_scale_x, scissoring_scale_y;
GetFramebufferScaling(true, scissoring_scale_x, scissoring_scale_y);
GetScissorScaling(scissoring_scale_x, scissoring_scale_y);
if (config::Widescreen && !config::Rotate90)
if (config::Widescreen && !config::Rotate90 && !config::EmulateFramebuffer)
{
sidebarWidth = (1 - dcViewport.x / dcViewport.y * renderViewport.y / renderViewport.x) / 2;
if (config::SuperWidescreen)
@ -185,34 +210,25 @@ public:
}
private:
void GetFramebufferScaling(bool scissor, float& scale_x, float& scale_y)
void GetScissorScaling(float& scale_x, float& scale_y) const
{
scale_x = 1.f;
scale_y = 1.f;
if (!renderingContext->isRTT && !renderingContext->isRenderFramebuffer)
if (!renderingContext->isRTT && !config::EmulateFramebuffer)
{
if (!scissor && (FB_R_CTRL.vclk_div == 0 && SPG_CONTROL.interlace == 0))
scale_y /= 2.f;
if (SCALER_CTL.vscalefactor > 0x400)
{
// Interlace mode A (single framebuffer)
if (SCALER_CTL.interlace == 0)
scale_y *= roundf((float)SCALER_CTL.vscalefactor / 0x400);
else if (SCALER_CTL.interlace == 1 && scissor)
// Interlace mode B (alternating framebuffers)
scale_y *= roundf((float)SCALER_CTL.vscalefactor / 0x400);
}
// VO pixel doubling is done after fb rendering/clipping
if (VO_CONTROL.pixel_double && !scissor)
scale_x /= 2.f;
// the X Scaler halves the horizontal resolution but
// before clipping/scissoring
scale_y *= std::round(SCALER_CTL.vscalefactor / 1024.f);
if (SCALER_CTL.hscale)
scale_x *= 2.f;
}
else if (config::EmulateFramebuffer)
{
if (SCALER_CTL.hscale)
scale_x *= 2.f;
if (SCALER_CTL.vscalefactor > 0x401 || SCALER_CTL.vscalefactor < 0x400)
scale_y *= SCALER_CTL.vscalefactor / 1024.f;
}
}
const rend_context *renderingContext = nullptr;
@ -222,29 +238,96 @@ private:
glm::mat4 viewportMatrix;
glm::vec2 dcViewport;
glm::vec2 renderViewport;
float scale_x = 0;
float scale_y = 0;
float sidebarWidth = 0;
};
inline static void getScaledFramebufferSize(int& width, int& height)
{
getPvrFramebufferSize(width, height);
if (!config::EmulateFramebuffer)
{
float upscaling = config::RenderResolution / 480.f;
float w = width * upscaling;
float h = height * upscaling;
if (config::Widescreen && !config::Rotate90)
{
if (config::SuperWidescreen)
w *= (float)settings.display.width / settings.display.height / 4.f * 3.f;
else
w *= 4.f / 3.f;
}
if (!config::Rotate90)
w = std::round(w / 2.f) * 2.f;
h = std::round(h);
width = w;
height = h;
}
}
inline static float getOutputFramebufferAspectRatio()
{
float renderAR;
int w,h;
getPvrFramebufferSize(w, h);
float width = w;
float height = h;
width *= 1 + VO_CONTROL.pixel_double;
width /= 1 + SCALER_CTL.hscale;
height *= 1 + (FB_R_CTRL.vclk_div == 0 && SPG_CONTROL.interlace == 0);
height *= 1 + (FB_R_CTRL.fb_line_double);
if (SCALER_CTL.vscalefactor != 0
&& (SCALER_CTL.vscalefactor > 1025 || SCALER_CTL.vscalefactor < 1024)
&& SPG_CONTROL.interlace == 0)
{
if (config::EmulateFramebuffer)
height *= 1024.f / SCALER_CTL.vscalefactor;
else if (SCALER_CTL.vscalefactor > 1025)
height *= std::round(1024.f / SCALER_CTL.vscalefactor);
}
float renderAR = width / height;
if (config::Rotate90)
{
renderAR = 3.f / 4.f;
renderAR = 1 / renderAR;
}
else
{
if (config::Widescreen)
if (config::Widescreen && !config::EmulateFramebuffer)
{
if (config::SuperWidescreen)
renderAR = (float)settings.display.width / settings.display.height;
else
renderAR = 16.f / 9.f;
renderAR *= 4 / 3.f;
}
else
renderAR = 4.f / 3.f;
}
return renderAR * config::ScreenStretching / 100.f;
}
inline static float getDCFramebufferAspectRatio()
{
int width = FB_R_SIZE.fb_x_size + 1; // in 32-bit words
int height = FB_R_SIZE.fb_y_size + 1;
switch (FB_R_CTRL.fb_depth)
{
case fbde_0555:
case fbde_565:
width *= 2;
break;
case fbde_888:
width = width * 4 / 3;
break;
case fbde_C888:
default:
break;
}
width *= 1 + VO_CONTROL.pixel_double;
height *= 1 + (FB_R_CTRL.vclk_div == 0 && SPG_CONTROL.interlace == 0);
height *= 1 + (FB_R_CTRL.fb_line_double);
height *= 1 + SPG_CONTROL.interlace;
float aspectRatio = (float)width / height;
if (config::Rotate90)
aspectRatio = 1 / aspectRatio;
return aspectRatio * config::ScreenStretching / 100.f;
}

View File

@ -22,20 +22,34 @@
#include "utils.h"
#include "vulkan_context.h"
BufferData::BufferData(vk::DeviceSize size, const vk::BufferUsageFlags& usage, const vk::MemoryPropertyFlags& propertyFlags)
: bufferSize(size), m_usage(usage), m_propertyFlags(propertyFlags)
BufferData::BufferData(vk::DeviceSize size, vk::BufferUsageFlags usage, vk::MemoryPropertyFlags propertyFlags)
: bufferSize(size), m_usage(usage)
{
VulkanContext *context = VulkanContext::Instance();
buffer = context->GetDevice().createBufferUnique(vk::BufferCreateInfo(vk::BufferCreateFlags(), size, usage));
VmaAllocationCreateInfo allocInfo = {
(propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent) ? VMA_ALLOCATION_CREATE_MAPPED_BIT : (VmaAllocationCreateFlags)0,
(propertyFlags & vk::MemoryPropertyFlagBits::eDeviceLocal) ? VmaMemoryUsage::VMA_MEMORY_USAGE_GPU_ONLY : VmaMemoryUsage::VMA_MEMORY_USAGE_CPU_TO_GPU
};
VmaAllocationCreateInfo allocInfo {};
if (propertyFlags & vk::MemoryPropertyFlagBits::eDeviceLocal)
{
allocInfo.requiredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
}
else
{
// FIXME VMA_ALLOCATION_CREATE_MAPPED_BIT ?
#ifdef __APPLE__
if (!(propertyFlags & vk::MemoryPropertyFlagBits::eDeviceLocal))
// cpu memory management is fucked up with moltenvk
allocInfo.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
// host coherent memory not supported on apple platforms
propertyFlags &= ~vk::MemoryPropertyFlagBits::eHostCoherent;
#endif
if (propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible)
{
allocInfo.requiredFlags = VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT;
if (propertyFlags & vk::MemoryPropertyFlagBits::eHostCached)
allocInfo.preferredFlags |= VK_MEMORY_PROPERTY_HOST_CACHED_BIT;
if (propertyFlags & vk::MemoryPropertyFlagBits::eHostCoherent)
allocInfo.preferredFlags |= VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
}
}
allocation = context->GetAllocator().AllocateForBuffer(*buffer, allocInfo);
}

View File

@ -25,14 +25,9 @@
struct BufferData
{
BufferData(vk::DeviceSize size, const vk::BufferUsageFlags& usage,
const vk::MemoryPropertyFlags& propertyFlags =
vk::MemoryPropertyFlagBits::eHostVisible
#ifndef __APPLE__
// host coherent memory not supported on apple platforms
| vk::MemoryPropertyFlagBits::eHostCoherent
#endif
);
BufferData(vk::DeviceSize size, vk::BufferUsageFlags usage,
vk::MemoryPropertyFlags propertyFlags =
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCoherent);
~BufferData()
{
buffer.reset();
@ -40,7 +35,6 @@ struct BufferData
void upload(u32 size, const void *data, u32 bufOffset = 0) const
{
verify((bool)(m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible));
verify(bufOffset + size <= bufferSize);
void* dataPtr = (u8 *)allocation.MapMemory() + bufOffset;
@ -50,8 +44,6 @@ struct BufferData
void upload(size_t count, const u32 *sizes, const void * const *data, u32 bufOffset = 0) const
{
verify((bool)(m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible));
u32 totalSize = 0;
for (size_t i = 0; i < count; i++)
totalSize += sizes[i];
@ -68,7 +60,6 @@ struct BufferData
void download(u32 size, void *data, u32 bufOffset = 0) const
{
verify((bool)(m_propertyFlags & vk::MemoryPropertyFlagBits::eHostVisible));
verify(bufOffset + size <= bufferSize);
void* dataPtr = (u8 *)allocation.MapMemory() + bufOffset;
@ -91,7 +82,6 @@ struct BufferData
private:
vk::BufferUsageFlags m_usage;
vk::MemoryPropertyFlags m_propertyFlags;
};
class BufferPacker

View File

@ -18,47 +18,6 @@
*/
#pragma once
#include "vulkan_context.h"
#include <array>
template<int Size>
class DescSetAlloc
{
public:
void setLayout(vk::DescriptorSetLayout layout) {
this->layout = layout;
}
void setAllocChunk(int size) {
this->allocChunk = size;
}
void nextFrame()
{
index = (index + 1) % Size;
for (auto& descset : descSetsInFlight[index])
descSets.emplace_back(std::move(descset));
descSetsInFlight[index].clear();
}
vk::DescriptorSet alloc()
{
if (descSets.empty())
{
std::vector<vk::DescriptorSetLayout> layouts(allocChunk, layout);
descSets = VulkanContext::Instance()->GetDevice().allocateDescriptorSetsUnique(
vk::DescriptorSetAllocateInfo(VulkanContext::Instance()->GetDescriptorPool(), (u32)layouts.size(), &layouts[0]));
}
descSetsInFlight[index].emplace_back(std::move(descSets.back()));
descSets.pop_back();
return *descSetsInFlight[index].back();
}
private:
vk::DescriptorSetLayout layout;
std::vector<vk::UniqueDescriptorSet> descSets;
std::array<std::vector<vk::UniqueDescriptorSet>, Size> descSetsInFlight;
int index = 0;
int allocChunk = 10;
};
class DynamicDescSetAlloc
{

View File

@ -62,8 +62,8 @@ TileClipping BaseDrawer::SetTileClip(u32 val, vk::Rect2D& clipRect)
void BaseDrawer::SetBaseScissor(const vk::Extent2D& viewport)
{
bool wide_screen_on = config::Widescreen && !pvrrc.isRenderFramebuffer
&& !matrices.IsClipped() && !config::Rotate90;
bool wide_screen_on = config::Widescreen
&& !matrices.IsClipped() && !config::Rotate90 && !config::EmulateFramebuffer;
if (!wide_screen_on)
{
float width;
@ -103,6 +103,76 @@ void BaseDrawer::SetBaseScissor(const vk::Extent2D& viewport)
}
}
void BaseDrawer::scaleAndWriteFramebuffer(vk::CommandBuffer commandBuffer, FramebufferAttachment *finalFB)
{
u32 width = (TA_GLOB_TILE_CLIP.tile_x_num + 1) * 32;
u32 height = (TA_GLOB_TILE_CLIP.tile_y_num + 1) * 32;
float xscale = SCALER_CTL.hscale == 1 ? 0.5f : 1.f;
float yscale = 1024.f / SCALER_CTL.vscalefactor;
if (std::abs(yscale - 1.f) < 0.01f)
yscale = 1.f;
FramebufferAttachment *scaledFB = nullptr;
if (xscale != 1.f || yscale != 1.f)
{
u32 scaledW = width * xscale;
u32 scaledH = height * yscale;
scaledFB = new FramebufferAttachment(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice());
scaledFB->Init(scaledW, scaledH, vk::Format::eR8G8B8A8Unorm, vk::ImageUsageFlagBits::eTransferSrc | vk::ImageUsageFlagBits::eTransferDst);
setImageLayout(commandBuffer, scaledFB->GetImage(), vk::Format::eR8G8B8A8Unorm, 1, vk::ImageLayout::eUndefined,
vk::ImageLayout::eTransferDstOptimal);
vk::ImageBlit imageBlit;
imageBlit.setSrcOffsets({ vk::Offset3D(0, 0, 0), vk::Offset3D(width, height, 1) });
imageBlit.setSrcSubresource(vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1));
imageBlit.setDstOffsets({ vk::Offset3D(0, 0, 0), vk::Offset3D(scaledW, scaledH, 1) });
imageBlit.setDstSubresource(imageBlit.srcSubresource);
commandBuffer.blitImage(finalFB->GetImage(), vk::ImageLayout::eTransferSrcOptimal, scaledFB->GetImage(), vk::ImageLayout::eTransferDstOptimal,
1, &imageBlit, vk::Filter::eLinear);
setImageLayout(commandBuffer, scaledFB->GetImage(), vk::Format::eR8G8B8A8Unorm, 1, vk::ImageLayout::eTransferDstOptimal,
vk::ImageLayout::eTransferSrcOptimal);
finalFB = scaledFB;
width = scaledW;
height = scaledH;
}
vk::BufferImageCopy copyRegion(0, width, height, vk::ImageSubresourceLayers(vk::ImageAspectFlagBits::eColor, 0, 0, 1), vk::Offset3D(0, 0, 0),
vk::Extent3D(width, height, 1));
commandBuffer.copyImageToBuffer(finalFB->GetImage(), vk::ImageLayout::eTransferSrcOptimal,
*finalFB->GetBufferData()->buffer, copyRegion);
vk::BufferMemoryBarrier bufferMemoryBarrier(
vk::AccessFlagBits::eTransferWrite,
vk::AccessFlagBits::eHostRead,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
*finalFB->GetBufferData()->buffer,
0,
VK_WHOLE_SIZE);
commandBuffer.pipelineBarrier(vk::PipelineStageFlagBits::eTransfer,
vk::PipelineStageFlagBits::eHost, {}, nullptr, bufferMemoryBarrier, nullptr);
commandBuffer.end();
commandPool->EndFrame();
vk::Fence fence = commandPool->GetCurrentFence();
GetContext()->GetDevice().waitForFences(1, &fence, true, UINT64_MAX);
PixelBuffer<u32> tmpBuf;
tmpBuf.init(width, height);
finalFB->GetBufferData()->download(width * height * 4, tmpBuf.data());
WriteFramebuffer(width, height, (u8 *)tmpBuf.data(), pvrrc.fb_W_SOF1 & VRAM_MASK,
pvrrc.fb_W_CTRL, pvrrc.fb_W_LINESTRIDE * 8, pvrrc.fb_X_CLIP, pvrrc.fb_Y_CLIP);
delete scaledFB;
}
void Drawer::DrawPoly(const vk::CommandBuffer& cmdBuffer, u32 listType, bool sortTriangles, const PolyParam& poly, u32 first, u32 count)
{
vk::Rect2D scissorRect;
@ -557,10 +627,11 @@ void ScreenDrawer::Init(SamplerManager *samplerManager, ShaderManager *shaderMan
{
vk::AttachmentDescription attachmentDescriptions[] = {
// Color attachment
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetColorFormat(), vk::SampleCountFlagBits::e1,
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), vk::Format::eR8G8B8A8Unorm, vk::SampleCountFlagBits::e1,
vk::AttachmentLoadOp::eLoad, vk::AttachmentStoreOp::eStore,
vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,
vk::ImageLayout::eShaderReadOnlyOptimal, vk::ImageLayout::eShaderReadOnlyOptimal),
config::EmulateFramebuffer ? vk::ImageLayout::eTransferSrcOptimal : vk::ImageLayout::eShaderReadOnlyOptimal,
config::EmulateFramebuffer ? vk::ImageLayout::eTransferSrcOptimal : vk::ImageLayout::eShaderReadOnlyOptimal),
// Depth attachment
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetDepthFormat(), vk::SampleCountFlagBits::e1,
vk::AttachmentLoadOp::eClear, vk::AttachmentStoreOp::eDontCare,
@ -610,8 +681,12 @@ void ScreenDrawer::Init(SamplerManager *samplerManager, ShaderManager *shaderMan
{
colorAttachments.push_back(std::unique_ptr<FramebufferAttachment>(
new FramebufferAttachment(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice())));
colorAttachments.back()->Init(viewport.width, viewport.height, GetContext()->GetColorFormat(),
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled);
vk::ImageUsageFlags usage = vk::ImageUsageFlagBits::eColorAttachment;
if (config::EmulateFramebuffer)
usage |= vk::ImageUsageFlagBits::eTransferSrc;
else
usage |= vk::ImageUsageFlagBits::eSampled;
colorAttachments.back()->Init(viewport.width, viewport.height, vk::Format::eR8G8B8A8Unorm, usage);
attachments[0] = colorAttachments.back()->GetImageView();
vk::FramebufferCreateInfo createInfo(vk::FramebufferCreateFlags(), *renderPassLoad,
ARRAY_SIZE(attachments), attachments, viewport.width, viewport.height, 1);
@ -630,13 +705,15 @@ void ScreenDrawer::Init(SamplerManager *samplerManager, ShaderManager *shaderMan
vk::CommandBuffer ScreenDrawer::BeginRenderPass()
{
NewImage();
vk::CommandBuffer commandBuffer = commandPool->Allocate();
commandBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
if (transitionNeeded[GetCurrentImage()])
{
setImageLayout(commandBuffer, colorAttachments[GetCurrentImage()]->GetImage(), GetContext()->GetColorFormat(),
1, vk::ImageLayout::eUndefined, vk::ImageLayout::eShaderReadOnlyOptimal);
setImageLayout(commandBuffer, colorAttachments[GetCurrentImage()]->GetImage(), vk::Format::eR8G8B8A8Unorm,
1, vk::ImageLayout::eUndefined,
config::EmulateFramebuffer ? vk::ImageLayout::eTransferSrcOptimal : vk::ImageLayout::eShaderReadOnlyOptimal);
transitionNeeded[GetCurrentImage()] = false;
}
@ -659,9 +736,16 @@ vk::CommandBuffer ScreenDrawer::BeginRenderPass()
void ScreenDrawer::EndRenderPass()
{
currentCommandBuffer.endRenderPass();
currentCommandBuffer.end();
if (config::EmulateFramebuffer)
{
scaleAndWriteFramebuffer(currentCommandBuffer, colorAttachments[GetCurrentImage()].get());
}
else
{
currentCommandBuffer.end();
commandPool->EndFrame();
}
currentCommandBuffer = nullptr;
commandPool->EndFrame();
Drawer::EndRenderPass();
frameRendered = true;
}

View File

@ -42,8 +42,9 @@ protected:
VulkanContext *GetContext() const { return VulkanContext::Instance(); }
TileClipping SetTileClip(u32 val, vk::Rect2D& clipRect);
void SetBaseScissor(const vk::Extent2D& viewport = vk::Extent2D());
void scaleAndWriteFramebuffer(vk::CommandBuffer commandBuffer, FramebufferAttachment *finalFB);
void SetScissor(const vk::CommandBuffer& cmdBuffer, const vk::Rect2D& scissor)
void SetScissor(vk::CommandBuffer cmdBuffer, const vk::Rect2D& scissor)
{
if (scissor != currentScissor)
{
@ -170,6 +171,13 @@ class Drawer : public BaseDrawer
{
public:
virtual ~Drawer() = default;
void Term()
{
descriptorSets.term();
mainBuffers.clear();
}
bool Draw(const Texture *fogTexture, const Texture *paletteTexture);
virtual void EndRenderPass() { renderPass++; }
vk::CommandBuffer GetCurrentCommandBuffer() const { return currentCommandBuffer; }
@ -259,6 +267,18 @@ class ScreenDrawer : public Drawer
{
public:
void Init(SamplerManager *samplerManager, ShaderManager *shaderManager, const vk::Extent2D& viewport);
void Term()
{
screenPipelineManager.reset();
renderPassLoad.reset();
renderPassClear.reset();
framebuffers.clear();
colorAttachments.clear();
depthAttachment.reset();
Drawer::Term();
}
vk::RenderPass GetRenderPass() const { return *renderPassClear; }
void EndRenderPass() override;
bool PresentFrame()
@ -268,7 +288,6 @@ public:
frameRendered = false;
GetContext()->PresentFrame(colorAttachments[GetCurrentImage()]->GetImage(),
colorAttachments[GetCurrentImage()]->GetImageView(), viewport);
NewImage();
return true;
}
@ -296,6 +315,16 @@ class TextureDrawer : public Drawer
{
public:
void Init(SamplerManager *samplerManager, ShaderManager *shaderManager, TextureCache *textureCache);
void Term()
{
rttPipelineManager.reset();
framebuffers.clear();
colorAttachment.reset();
depthAttachment.reset();
Drawer::Term();
}
void EndRenderPass() override;
protected:

View File

@ -444,7 +444,7 @@ void OITDrawer::MakeBuffers(int width, int height)
attachment.reset();
attachment = std::unique_ptr<FramebufferAttachment>(
new FramebufferAttachment(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice()));
attachment->Init(maxWidth, maxHeight, GetColorFormat(),
attachment->Init(maxWidth, maxHeight, vk::Format::eR8G8B8A8Unorm,
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eInputAttachment);
}
@ -483,12 +483,18 @@ void OITScreenDrawer::MakeFramebuffers(const vk::Extent2D& viewport)
finalColorAttachments.clear();
transitionNeeded.clear();
clearNeeded.clear();
vk::ImageUsageFlags usage = vk::ImageUsageFlagBits::eColorAttachment;
if (config::EmulateFramebuffer)
usage |= vk::ImageUsageFlagBits::eTransferSrc;
else
usage |= vk::ImageUsageFlagBits::eSampled;
while (finalColorAttachments.size() < GetSwapChainSize())
{
finalColorAttachments.push_back(std::unique_ptr<FramebufferAttachment>(
new FramebufferAttachment(GetContext()->GetPhysicalDevice(), GetContext()->GetDevice())));
finalColorAttachments.back()->Init(viewport.width, viewport.height, GetContext()->GetColorFormat(),
vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eSampled);
finalColorAttachments.back()->Init(viewport.width, viewport.height, vk::Format::eR8G8B8A8Unorm,
usage);
vk::ImageView attachments[] = {
finalColorAttachments.back()->GetImageView(),
colorAttachments[0]->GetImageView(),
@ -659,18 +665,16 @@ void OITTextureDrawer::EndFrame()
vk::CommandBuffer OITScreenDrawer::NewFrame()
{
if (frameRendered)
{
// in case the previous image was never presented
frameRendered = false;
NewImage();
}
frameRendered = false;
NewImage();
vk::CommandBuffer commandBuffer = commandPool->Allocate();
commandBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
if (transitionNeeded[GetCurrentImage()])
{
setImageLayout(commandBuffer, finalColorAttachments[GetCurrentImage()]->GetImage(), GetColorFormat(), 1, vk::ImageLayout::eUndefined, vk::ImageLayout::eShaderReadOnlyOptimal);
setImageLayout(commandBuffer, finalColorAttachments[GetCurrentImage()]->GetImage(), vk::Format::eR8G8B8A8Unorm, 1,
vk::ImageLayout::eUndefined,
config::EmulateFramebuffer ? vk::ImageLayout::eTransferSrcOptimal : vk::ImageLayout::eShaderReadOnlyOptimal);
transitionNeeded[GetCurrentImage()] = false;
}
matrices.CalcMatrices(&pvrrc, viewport.extent.width, viewport.extent.height);

View File

@ -70,6 +70,8 @@ protected:
depthAttachments[1].reset();
mainBuffers.clear();
descriptorSets.term();
maxWidth = 0;
maxHeight = 0;
}
int GetCurrentImage() const { return imageIndex; }
@ -104,7 +106,6 @@ protected:
};
void MakeBuffers(int width, int height);
virtual vk::Format GetColorFormat() const = 0;
virtual vk::Framebuffer GetFinalFramebuffer() const = 0;
vk::Rect2D viewport;
@ -177,12 +178,20 @@ public:
}
vk::CommandBuffer NewFrame() override;
void EndFrame() override
{
currentCommandBuffer.endRenderPass();
currentCommandBuffer.end();
if (config::EmulateFramebuffer)
{
scaleAndWriteFramebuffer(currentCommandBuffer, finalColorAttachments[GetCurrentImage()].get());
}
else
{
currentCommandBuffer.end();
commandPool->EndFrame();
}
currentCommandBuffer = nullptr;
commandPool->EndFrame();
OITDrawer::EndFrame();
frameRendered = true;
}
@ -194,7 +203,6 @@ public:
frameRendered = false;
GetContext()->PresentFrame(finalColorAttachments[GetCurrentImage()]->GetImage(),
finalColorAttachments[GetCurrentImage()]->GetImageView(), viewport.extent);
NewImage();
return true;
}
@ -203,7 +211,6 @@ public:
protected:
vk::Framebuffer GetFinalFramebuffer() const override { return *framebuffers[GetCurrentImage()]; }
vk::Format GetColorFormat() const override { return GetContext()->GetColorFormat(); }
private:
void MakeFramebuffers(const vk::Extent2D& viewport);
@ -241,7 +248,6 @@ public:
protected:
vk::CommandBuffer NewFrame() override;
vk::Framebuffer GetFinalFramebuffer() const override { return *framebuffers[GetCurrentImage()]; }
vk::Format GetColorFormat() const override { return vk::Format::eR8G8B8A8Unorm; }
private:
u32 textureAddr = 0;

View File

@ -38,7 +38,7 @@ public:
screenDrawer.Init(&samplerManager, &oitShaderManager, &oitBuffers, viewport);
screenDrawer.SetCommandPool(&texCommandPool);
BaseInit(screenDrawer.GetRenderPass(), 2);
emulateFramebuffer = config::EmulateFramebuffer;
return true;
}
@ -49,15 +49,6 @@ public:
return false;
}
void Resize(int w, int h) override
{
if ((u32)w == viewport.width && (u32)h == viewport.height)
return;
BaseVulkanRenderer::Resize(w, h);
GetContext()->WaitIdle();
screenDrawer.Init(&samplerManager, &oitShaderManager, &oitBuffers, viewport);
}
void Term() override
{
DEBUG_LOG(RENDERER, "OITVulkanRenderer::Term");
@ -65,22 +56,35 @@ public:
screenDrawer.Term();
textureDrawer.Term();
oitBuffers.Term();
oitShaderManager.term();
samplerManager.term();
BaseVulkanRenderer::Term();
}
bool Render() override
{
try {
if (emulateFramebuffer != config::EmulateFramebuffer)
{
VulkanContext::Instance()->WaitIdle();
screenDrawer.Term();
screenDrawer.Init(&samplerManager, &oitShaderManager, &oitBuffers, viewport);
BaseInit(screenDrawer.GetRenderPass(), 2);
emulateFramebuffer = config::EmulateFramebuffer;
}
OITDrawer *drawer;
if (pvrrc.isRTT)
drawer = &textureDrawer;
else
else {
resize(pvrrc.framebufferWidth, pvrrc.framebufferHeight);
drawer = &screenDrawer;
}
drawer->Draw(fogTexture.get(), paletteTexture.get());
#ifdef LIBRETRO
if (!pvrrc.isRTT)
overlay->Draw(screenDrawer.GetCurrentCommandBuffer(), viewport, (int)config::RenderResolution / 480.f, true, true);
overlay->Draw(screenDrawer.GetCurrentCommandBuffer(), viewport,
config::EmulateFramebuffer ? 1 : (int)config::RenderResolution / 480.f, true, true);
#endif
drawer->EndFrame();
@ -96,7 +100,20 @@ public:
bool Present() override
{
return screenDrawer.PresentFrame();
if (config::EmulateFramebuffer)
return presentFramebuffer();
else
return screenDrawer.PresentFrame();
}
protected:
void resize(int w, int h) override
{
if ((u32)w == viewport.width && (u32)h == viewport.height)
return;
BaseVulkanRenderer::resize(w, h);
GetContext()->WaitIdle();
screenDrawer.Init(&samplerManager, &oitShaderManager, &oitBuffers, viewport);
}
private:
@ -105,6 +122,7 @@ private:
OITShaderManager oitShaderManager;
OITScreenDrawer screenDrawer;
OITTextureDrawer textureDrawer;
bool emulateFramebuffer = false;
};
Renderer* rend_OITVulkan()

View File

@ -26,7 +26,7 @@ vk::UniqueRenderPass RenderPasses::MakeRenderPass(bool initial, bool last)
// Swap chain image
GetAttachment0Description(initial, last),
// OP+PT color attachment
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetColorFormat(), vk::SampleCountFlagBits::e1,
vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), vk::Format::eR8G8B8A8Unorm, vk::SampleCountFlagBits::e1,
initial ? vk::AttachmentLoadOp::eClear : vk::AttachmentLoadOp::eLoad,
last ? vk::AttachmentStoreOp::eDontCare : vk::AttachmentStoreOp::eStore,
vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,

View File

@ -43,12 +43,13 @@ protected:
vk::UniqueRenderPass MakeRenderPass(bool initial, bool last);
virtual vk::AttachmentDescription GetAttachment0Description(bool initial, bool last) const
{
return vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), GetContext()->GetColorFormat(), vk::SampleCountFlagBits::e1,
return vk::AttachmentDescription(vk::AttachmentDescriptionFlags(), vk::Format::eR8G8B8A8Unorm, vk::SampleCountFlagBits::e1,
vk::AttachmentLoadOp::eLoad, vk::AttachmentStoreOp::eStore,
vk::AttachmentLoadOp::eDontCare, vk::AttachmentStoreOp::eDontCare,
vk::ImageLayout::eShaderReadOnlyOptimal, vk::ImageLayout::eShaderReadOnlyOptimal);
config::EmulateFramebuffer && last ? vk::ImageLayout::eTransferSrcOptimal : vk::ImageLayout::eShaderReadOnlyOptimal,
config::EmulateFramebuffer && last ? vk::ImageLayout::eTransferSrcOptimal : vk::ImageLayout::eShaderReadOnlyOptimal);
}
virtual vk::Format GetColorFormat() const { return GetContext()->GetColorFormat(); }
virtual std::vector<vk::SubpassDependency> GetSubpassDependencies() const
{
std::vector<vk::SubpassDependency> deps;
@ -72,7 +73,7 @@ protected:
vk::ImageLayout::eUndefined,
config::RenderToTextureBuffer && last ? vk::ImageLayout::eTransferSrcOptimal : vk::ImageLayout::eShaderReadOnlyOptimal);
}
vk::Format GetColorFormat() const override { return vk::Format::eR8G8B8A8Unorm; }
std::vector<vk::SubpassDependency> GetSubpassDependencies() const override
{
std::vector<vk::SubpassDependency> deps;

View File

@ -124,6 +124,20 @@ public:
return *clearShader;
}
void term()
{
vertexShaders.clear();
fragmentShaders.clear();
modVolVertexShaders.clear();
modVolShaders[0].reset();
modVolShaders[1].reset();
trModVolShaders.clear();
finalVertexShader.reset();
finalFragmentShader.reset();
clearShader.reset();
}
private:
template<typename T>
vk::ShaderModule getShader(std::map<u32, vk::UniqueShaderModule>& map, T params)

View File

@ -420,6 +420,15 @@ public:
GetContext()->GetDevice().updateDescriptorSets(writeDescriptorSets, nullptr);
}
void Term()
{
descriptorSet.reset();
pipeline.reset();
sampler.reset();
pipelineLayout.reset();
descSetLayout.reset();
}
vk::Pipeline GetPipeline()
{
if (!pipeline)

View File

@ -158,6 +158,21 @@ public:
return *osdFragmentShader;
}
void term()
{
vertexShaders.clear();
fragmentShaders.clear();
modVolVertexShaders.clear();
modVolShaders[0].reset();
modVolShaders[1].reset();
quadVertexShader.reset();
quadRotateVertexShader.reset();
quadFragmentShader.reset();
quadNoAlphaFragmentShader.reset();
osdVertexShader.reset();
osdFragmentShader.reset();
}
private:
template<typename T>
vk::ShaderModule getShader(std::map<u32, vk::UniqueShaderModule>& map, T params)

View File

@ -406,7 +406,8 @@ void FramebufferAttachment::Init(u32 width, u32 height, vk::Format format, const
if (usage & vk::ImageUsageFlagBits::eTransferSrc)
{
stagingBufferData = std::unique_ptr<BufferData>(new BufferData(width * height * 4,
vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst));
vk::BufferUsageFlagBits::eTransferSrc | vk::BufferUsageFlagBits::eTransferDst,
vk::MemoryPropertyFlagBits::eHostVisible | vk::MemoryPropertyFlagBits::eHostCached | vk::MemoryPropertyFlagBits::eHostCoherent));
}
vk::ImageCreateInfo imageCreateInfo(vk::ImageCreateFlags(), vk::ImageType::e2D, format, vk::Extent3D(extent, 1), 1, 1, vk::SampleCountFlagBits::e1,
vk::ImageTiling::eOptimal, usage,
@ -418,15 +419,18 @@ void FramebufferAttachment::Init(u32 width, u32 height, vk::Format format, const
allocCreateInfo.preferredFlags = VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
allocation = VulkanContext::Instance()->GetAllocator().AllocateForImage(*image, allocCreateInfo);
vk::ImageViewCreateInfo imageViewCreateInfo(vk::ImageViewCreateFlags(), image.get(), vk::ImageViewType::e2D,
format, vk::ComponentMapping(), vk::ImageSubresourceRange(depth ? vk::ImageAspectFlagBits::eDepth : vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
imageView = device.createImageViewUnique(imageViewCreateInfo);
if ((usage & vk::ImageUsageFlagBits::eDepthStencilAttachment) && (usage & vk::ImageUsageFlagBits::eInputAttachment))
if ((usage & vk::ImageUsageFlagBits::eColorAttachment) || (usage & vk::ImageUsageFlagBits::eDepthStencilAttachment))
{
// Also create an imageView for the stencil
imageViewCreateInfo.subresourceRange = vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eStencil, 0, 1, 0, 1);
stencilView = device.createImageViewUnique(imageViewCreateInfo);
vk::ImageViewCreateInfo imageViewCreateInfo(vk::ImageViewCreateFlags(), image.get(), vk::ImageViewType::e2D,
format, vk::ComponentMapping(), vk::ImageSubresourceRange(depth ? vk::ImageAspectFlagBits::eDepth : vk::ImageAspectFlagBits::eColor, 0, 1, 0, 1));
imageView = device.createImageViewUnique(imageViewCreateInfo);
if ((usage & vk::ImageUsageFlagBits::eDepthStencilAttachment) && (usage & vk::ImageUsageFlagBits::eInputAttachment))
{
// Also create an imageView for the stencil
imageViewCreateInfo.subresourceRange = vk::ImageSubresourceRange(vk::ImageAspectFlagBits::eStencil, 0, 1, 0, 1);
stencilView = device.createImageViewUnique(imageViewCreateInfo);
}
}
}

View File

@ -59,9 +59,11 @@ public:
u64 GetIntId() { return (u64)reinterpret_cast<uintptr_t>(this); }
std::string GetId() override { char s[20]; sprintf(s, "%p", this); return s; }
vk::ImageView GetImageView() const { return *imageView; }
vk::Image GetImage() const { return *image; }
vk::ImageView GetReadOnlyImageView() const { return readOnlyImageView ? readOnlyImageView : *imageView; }
void SetCommandBuffer(vk::CommandBuffer commandBuffer) { this->commandBuffer = commandBuffer; }
bool Force32BitTexture(TextureType type) const override { return !VulkanContext::Instance()->IsFormatSupported(type); }
vk::Extent2D getSize() const { return extent; }
private:
void Init(u32 width, u32 height, vk::Format format ,u32 dataSize, bool mipmapped, bool mipmapsIncluded);
@ -93,6 +95,10 @@ private:
class SamplerManager
{
public:
void term() {
samplers.clear();
}
vk::Sampler GetSampler(TSP tsp)
{
u32 samplerHash = tsp.full & TSP_Mask; // MipMapD, FilterMode, ClampU, ClampV, FlipU, FlipV

View File

@ -274,7 +274,7 @@ bool VulkanContext::init(retro_hw_render_interface_vulkan *retro_render_if)
retro_image.image_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
retro_image.create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
retro_image.create_info.pNext = nullptr;
retro_image.create_info.format = (VkFormat)colorFormat;
retro_image.create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
retro_image.create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
retro_image.create_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
retro_image.create_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;

View File

@ -78,7 +78,6 @@ public:
+ "." + std::to_string(VK_VERSION_MINOR(props.driverVersion))
+ "." + std::to_string(VK_VERSION_PATCH(props.driverVersion));
}
vk::Format GetColorFormat() const { return colorFormat; }
vk::Format GetDepthFormat() const { return depthFormat; }
static VulkanContext *Instance() { return contextInstance; }
bool SupportsSamplerAnisotropy() const { return samplerAnisotropy; }
@ -113,7 +112,6 @@ public:
bool dedicatedAllocationSupported = false;
private:
u32 vendorID = 0;
vk::Format colorFormat = vk::Format::eR8G8B8A8Unorm;
vk::UniqueDescriptorPool descriptorPool;

View File

@ -64,14 +64,20 @@ public:
return allocInfo.pMappedData;
void *p;
vmaMapMemory(allocator, allocation, &p);
VkMemoryPropertyFlags flags;
vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &flags);
if ((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) && (flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0)
vmaInvalidateAllocation(allocator, allocation, allocInfo.offset, allocInfo.size);
return p;
}
void UnmapMemory() const
{
if (allocInfo.pMappedData != nullptr)
return;
// Only needed (and useful) for non-host coherent memory
vmaFlushAllocation(allocator, allocation, allocInfo.offset, allocInfo.size);
VkMemoryPropertyFlags flags;
vmaGetMemoryTypeProperties(allocator, allocInfo.memoryType, &flags);
if ((flags & VK_MEMORY_PROPERTY_HOST_CACHED_BIT) && (flags & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == 0)
vmaFlushAllocation(allocator, allocation, allocInfo.offset, allocInfo.size);
vmaUnmapMemory(allocator, allocation);
}

View File

@ -392,17 +392,17 @@ bool VulkanContext::InitDevice()
#ifdef VK_DEBUG
else if (!strcmp(property.extensionName, VK_EXT_DEBUG_MARKER_EXTENSION_NAME))
{
NOTICE_LOG(RENDERER, "Debug extension %s available", property.extensionName);
NOTICE_LOG(RENDERER, "Debug extension %s available", property.extensionName.data());
deviceExtensions.push_back(VK_EXT_DEBUG_MARKER_EXTENSION_NAME);
}
else if(!strcmp(property.extensionName, VK_EXT_DEBUG_REPORT_EXTENSION_NAME))
{
NOTICE_LOG(RENDERER, "Debug extension %s available", property.extensionName);
NOTICE_LOG(RENDERER, "Debug extension %s available", property.extensionName.data());
deviceExtensions.push_back(VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
}
else if (!strcmp(property.extensionName, VK_EXT_DEBUG_UTILS_EXTENSION_NAME))
{
NOTICE_LOG(RENDERER, "Debug extension %s available", property.extensionName);
NOTICE_LOG(RENDERER, "Debug extension %s available", property.extensionName.data());
deviceExtensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
#endif

View File

@ -91,7 +91,6 @@ public:
}
std::string getDriverName() override;
std::string getDriverVersion() override;
vk::Format GetColorFormat() const { return colorFormat; }
vk::Format GetDepthFormat() const { return depthFormat; }
static VulkanContext *Instance() { return contextInstance; }
bool SupportsSamplerAnisotropy() const { return samplerAnisotropy; }

View File

@ -36,40 +36,46 @@ public:
screenDrawer.Init(&samplerManager, &shaderManager, viewport);
screenDrawer.SetCommandPool(&texCommandPool);
BaseInit(screenDrawer.GetRenderPass());
emulateFramebuffer = config::EmulateFramebuffer;
return true;
}
void Resize(int w, int h) override
{
if ((u32)w == viewport.width && (u32)h == viewport.height)
return;
BaseVulkanRenderer::Resize(w, h);
GetContext()->WaitIdle();
screenDrawer.Init(&samplerManager, &shaderManager, viewport);
}
void Term() override
{
DEBUG_LOG(RENDERER, "VulkanRenderer::Term");
GetContext()->WaitIdle();
screenDrawer.Term();
textureDrawer.Term();
samplerManager.term();
BaseVulkanRenderer::Term();
}
bool Render() override
{
try {
if (emulateFramebuffer != config::EmulateFramebuffer)
{
VulkanContext::Instance()->WaitIdle();
screenDrawer.Term();
screenDrawer.Init(&samplerManager, &shaderManager, viewport);
BaseInit(screenDrawer.GetRenderPass());
emulateFramebuffer = config::EmulateFramebuffer;
}
Drawer *drawer;
if (pvrrc.isRTT)
drawer = &textureDrawer;
else
else {
resize(pvrrc.framebufferWidth, pvrrc.framebufferHeight);
drawer = &screenDrawer;
}
drawer->Draw(fogTexture.get(), paletteTexture.get());
#ifdef LIBRETRO
if (!pvrrc.isRTT)
overlay->Draw(screenDrawer.GetCurrentCommandBuffer(), viewport, (int)config::RenderResolution / 480.f, true, true);
overlay->Draw(screenDrawer.GetCurrentCommandBuffer(), viewport,
config::EmulateFramebuffer ? 1 : (int)config::RenderResolution / 480.f, true, true);
#endif
drawer->EndRenderPass();
@ -85,13 +91,27 @@ public:
bool Present() override
{
return screenDrawer.PresentFrame();
if (config::EmulateFramebuffer)
return presentFramebuffer();
else
return screenDrawer.PresentFrame();
}
protected:
void resize(int w, int h) override
{
if ((u32)w == viewport.width && (u32)h == viewport.height)
return;
BaseVulkanRenderer::resize(w, h);
GetContext()->WaitIdle();
screenDrawer.Init(&samplerManager, &shaderManager, viewport);
}
private:
SamplerManager samplerManager;
ScreenDrawer screenDrawer;
TextureDrawer textureDrawer;
bool emulateFramebuffer = false;
};
Renderer* rend_Vulkan()

View File

@ -36,6 +36,7 @@ protected:
bool BaseInit(vk::RenderPass renderPass, int subpass = 0)
{
texCommandPool.Init();
fbCommandPool.Init();
#if defined(__ANDROID__) && !defined(LIBRETRO)
if (!vjoyTexture)
@ -61,9 +62,11 @@ protected:
vk::BufferUsageFlagBits::eVertexBuffer));
}
#endif
#ifdef LIBRETRO
quadPipeline = std::unique_ptr<QuadPipeline>(new QuadPipeline(false, false));
quadPipeline->Init(&shaderManager, renderPass, subpass);
framebufferDrawer = std::unique_ptr<QuadDrawer>(new QuadDrawer());
framebufferDrawer->Init(quadPipeline.get());
#ifdef LIBRETRO
overlay = std::unique_ptr<VulkanOverlay>(new VulkanOverlay());
overlay->Init(quadPipeline.get());
#endif
@ -78,15 +81,19 @@ public:
#ifdef LIBRETRO
overlay->Term();
overlay.reset();
quadPipeline.reset();
#endif
framebufferDrawer.reset();
quadPipeline.reset();
osdBuffer.reset();
osdPipeline.Term();
vjoyTexture.reset();
textureCache.Clear();
fogTexture = nullptr;
paletteTexture = nullptr;
texCommandPool.Term();
fbCommandPool.Term();
framebufferTextures.clear();
shaderManager.term();
}
BaseTextureCacheData *GetTexture(TSP tsp, TCW tcw) override
@ -126,11 +133,7 @@ public:
texCommandBuffer = texCommandPool.Allocate();
texCommandBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
bool result;
if (ctx->rend.isRenderFramebuffer)
result = RenderFramebuffer(ctx);
else
result = ta_parse(ctx);
bool result = ta_parse(ctx);
if (result)
{
@ -151,15 +154,10 @@ public:
return result;
}
void Resize(int w, int h) override
{
viewport.width = w;
viewport.height = h;
}
void ReInitOSD()
{
texCommandPool.Init();
fbCommandPool.Init();
#if defined(__ANDROID__) && !defined(LIBRETRO)
osdPipeline.Init(&shaderManager, vjoyTexture->GetImageView(), GetContext()->GetRenderPass());
#endif
@ -212,21 +210,8 @@ public:
#endif
}
protected:
BaseVulkanRenderer() : viewport(640, 480) {}
VulkanContext *GetContext() const { return VulkanContext::Instance(); }
bool RenderFramebuffer(TA_context* ctx)
void RenderFramebuffer(const FramebufferInfo& info) override
{
if (FB_R_SIZE.fb_x_size == 0 || FB_R_SIZE.fb_y_size == 0)
return false;
PixelBuffer<u32> pb;
int width;
int height;
ReadFramebuffer(pb, width, height);
if (framebufferTextures.size() != GetContext()->GetSwapChainSize())
framebufferTextures.resize(GetContext()->GetSwapChainSize());
std::unique_ptr<Texture>& curTexture = framebufferTextures[GetContext()->GetCurrentImageIndex()];
@ -235,72 +220,42 @@ protected:
curTexture = std::unique_ptr<Texture>(new Texture());
curTexture->tex_type = TextureType::_8888;
}
curTexture->SetCommandBuffer(texCommandBuffer);
curTexture->UploadToGPU(width, height, (u8*)pb.data(), false);
fbCommandPool.BeginFrame();
vk::CommandBuffer commandBuffer = fbCommandPool.Allocate();
commandBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit));
curTexture->SetCommandBuffer(commandBuffer);
if (info.fb_r_ctrl.fb_enable == 0 || info.vo_control.blank_video == 1)
{
// Video output disabled
u8 rgba[] { (u8)info.vo_border_col._red, (u8)info.vo_border_col._green, (u8)info.vo_border_col._blue, 255 };
curTexture->UploadToGPU(1, 1, rgba, false);
}
else
{
PixelBuffer<u32> pb;
int width;
int height;
ReadFramebuffer(info, pb, width, height);
curTexture->UploadToGPU(width, height, (u8*)pb.data(), false);
}
curTexture->SetCommandBuffer(nullptr);
commandBuffer.end();
fbCommandPool.EndFrame();
}
// Use background poly vtx and param
Vertex *vtx = ctx->rend.verts.head();
vtx[0].x = 0.f;
vtx[0].y = 0.f;
vtx[0].z = 0.1f;
vtx[0].u = 0.f;
vtx[0].v = 0.f;
protected:
BaseVulkanRenderer() : viewport(640, 480) {}
vtx[1] = vtx[0];
vtx[1].x = 640.f;
vtx[1].u = 1.f;
VulkanContext *GetContext() const { return VulkanContext::Instance(); }
vtx[2] = vtx[0];
vtx[2].y = 480.f;
vtx[2].v = 1.f;
vtx[3] = vtx[0];
vtx[3].x = 640.f;
vtx[3].y = 480.f;
vtx[3].u = 1.f;
vtx[3].v = 1.f;
u32 *idx = ctx->rend.idx.Append(4);
idx[0] = 0;
idx[1] = 1;
idx[2] = 2;
idx[3] = 3;
PolyParam *pp = ctx->rend.global_param_op.head();
pp->first = 0;
pp->count = 4;
pp->isp.full = 0;
pp->isp.DepthMode = 7;
pp->pcw.full = 0;
pp->pcw.Gouraud = 1;
pp->pcw.Texture = 1;
pp->tcw.full = 0;
pp->tcw.TexAddr = 0x1fffff;
pp->tcw1.full = (u32)-1;
pp->tsp.full = 0;
pp->tsp.FilterMode = 1;
pp->tsp.FogCtrl = 2;
pp->tsp.SrcInstr = 1;
pp->tsp1.full = (u32)-1;
pp->texture = curTexture.get();
pp->texture1 = nullptr;
pp->tileclip = 0;
RenderPass *pass = ctx->rend.render_passes.Append(1);
pass->autosort = false;
pass->mvo_count = 0;
pass->mvo_tr_count = 0;
pass->op_count = 1;
pass->pt_count = 0;
pass->tr_count = 0;
return true;
virtual void resize(int w, int h)
{
viewport.width = w;
viewport.height = h;
}
void CheckFogTexture()
@ -342,6 +297,17 @@ protected:
paletteTexture->SetCommandBuffer(nullptr);
}
bool presentFramebuffer()
{
if (GetContext()->GetCurrentImageIndex() >= (int)framebufferTextures.size())
return false;
Texture *fbTexture = framebufferTextures[GetContext()->GetCurrentImageIndex()].get();
if (fbTexture == nullptr)
return false;
GetContext()->PresentFrame(fbTexture->GetImage(), fbTexture->GetImageView(), fbTexture->getSize());
return true;
}
ShaderManager shaderManager;
std::unique_ptr<Texture> fogTexture;
std::unique_ptr<Texture> paletteTexture;
@ -353,8 +319,10 @@ protected:
TextureCache textureCache;
vk::Extent2D viewport;
vk::CommandBuffer texCommandBuffer;
std::unique_ptr<QuadPipeline> quadPipeline;
std::unique_ptr<QuadDrawer> framebufferDrawer;
CommandPool fbCommandPool;
#ifdef LIBRETRO
std::unique_ptr<VulkanOverlay> overlay;
std::unique_ptr<QuadPipeline> quadPipeline;
#endif
};

View File

@ -769,7 +769,6 @@ static void update_variables(bool first_startup)
if (!first_startup && previous_renderer != config::RendererType) {
rend_term_renderer();
rend_init_renderer();
rend_resize_renderer();
}
#if defined(HAVE_OIT) || defined(HAVE_VULKAN) || defined(HAVE_D3D11)
@ -1030,7 +1029,6 @@ static void update_variables(bool first_startup)
rotate_screen ^= rotate_game;
if (rotate_game)
config::Widescreen.override(false);
rend_resize_renderer();
if ((libretro_detect_vsync_swap_interval != prevDetectVsyncSwapInterval) &&
!libretro_detect_vsync_swap_interval &&
@ -1127,7 +1125,6 @@ void retro_reset()
config::Widescreen.override(false);
config::Rotate90 = false;
rend_resize_renderer();
retro_game_geometry geometry;
setGameGeometry(geometry);
environ_cb(RETRO_ENVIRONMENT_SET_GEOMETRY, &geometry);
@ -1147,7 +1144,6 @@ static void context_reset()
rend_term_renderer();
theGLContext.init();
rend_init_renderer();
rend_resize_renderer();
}
static void context_destroy()
@ -1613,7 +1609,6 @@ static void retro_vk_context_reset()
theVulkanContext.init((retro_hw_render_interface_vulkan *)vulkan);
rend_term_renderer();
rend_init_renderer();
rend_resize_renderer();
}
static void retro_vk_context_destroy()
@ -1747,7 +1742,6 @@ static void dx11_context_reset()
else if (config::RendererType != RenderType::DirectX11_OIT)
config::RendererType = RenderType::DirectX11;
rend_init_renderer();
rend_resize_renderer();
}
static void dx11_context_destroy()
@ -2413,7 +2407,7 @@ static void updateLightgunCoordinates(u32 port)
{
int x = input_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_X);
int y = input_cb(port, RETRO_DEVICE_LIGHTGUN, 0, RETRO_DEVICE_ID_LIGHTGUN_SCREEN_Y);
if (config::Widescreen && config::ScreenStretching == 100)
if (config::Widescreen && config::ScreenStretching == 100 && !config::EmulateFramebuffer)
mo_x_abs[port] = 640.f * ((x + 0x8000) * 4.f / 3.f / 0x10000 - (4.f / 3.f - 1.f) / 2.f);
else
mo_x_abs[port] = (x + 0x8000) * 640.f / 0x10000;

View File

@ -371,11 +371,25 @@ struct retro_core_option_v2_definition option_defs_us[] = {
"32",
},
#endif
{
CORE_OPTION_NAME "_emulate_framebuffer",
"Full framebuffer emulation",
NULL,
"Enable full framebuffer emulation in VRAM. This is useful for games that directly read or write the framebuffer in VRAM. When enabled, Internal Resolution is forced to 640x480 and performance may be severely impacted.",
NULL,
"video",
{
{ "disabled", NULL },
{ "enabled", NULL },
{ NULL, NULL },
},
"disabled",
},
{/* TODO: needs explanation */
CORE_OPTION_NAME "_enable_rttb",
"Enable RTT (Render To Texture) Buffer",
NULL,
"",
"Copy rendered textures back from the GPU to VRAM. This option is normally enabled for games that require it. When enabled, texture rendering upscaling is disabled and performance may be impacted.",
NULL,
"video",
{

View File

@ -91,6 +91,7 @@ Option<bool> PowerVR2Filter(CORE_OPTION_NAME "_pvr2_filtering");
Option<int64_t> PixelBufferSize("", 512 * 1024 * 1024);
IntOption PerPixelLayers(CORE_OPTION_NAME "_oit_layers");
Option<bool> NativeDepthInterpolation(CORE_OPTION_NAME "_native_depth_interpolation");
Option<bool> EmulateFramebuffer(CORE_OPTION_NAME "_emulate_framebuffer", false);
// Misc

View File

@ -117,7 +117,7 @@ std::pair<float, float> getCrosshairPosition(int playerNum)
{
float fx = lightgun_params[playerNum].x * config::RenderResolution * config::ScreenStretching / 480.f / 100.f;
float fy = lightgun_params[playerNum].y * config::RenderResolution / 480.f;
if (config::Widescreen && config::ScreenStretching == 100)
if (config::Widescreen && config::ScreenStretching == 100 && !config::EmulateFramebuffer)
fx += (480.f * 16.f / 9.f - 640.f) / 2.f * config::RenderResolution / 480.f;
return std::make_pair(fx, fy);