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:
parent
e3c260f4ca
commit
5722dc90f0
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -302,7 +302,6 @@ void DX11Context::handleDeviceLost()
|
|||
term();
|
||||
init(true);
|
||||
rend_init_renderer();
|
||||
rend_resize_renderer();
|
||||
}
|
||||
#endif // !LIBRETRO
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -120,7 +120,6 @@ void DXContext::Present()
|
|||
{
|
||||
renderer = new D3DRenderer();
|
||||
renderer->Init();
|
||||
rend_resize_renderer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -67,7 +67,6 @@ bool mainui_rend_frame()
|
|||
void mainui_init()
|
||||
{
|
||||
rend_init_renderer();
|
||||
rend_resize_renderer();
|
||||
}
|
||||
|
||||
void mainui_term()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue