mirror of
https://github.com/libretro/beetle-psx-libretro.git
synced 2024-11-27 02:40:31 +00:00
1043 lines
32 KiB
C++
1043 lines
32 KiB
C++
#include "rsx/rsx_lib_vulkan.h"
|
|
|
|
#include <stdint.h>
|
|
|
|
#include <functional>
|
|
#include <vector>
|
|
|
|
#include "rsx/rsx_intf.h" //FPS and audio sample rate macros
|
|
#include "parallel-psx/renderer/renderer.hpp"
|
|
#include "libretro_vulkan.h"
|
|
|
|
// #include "mednafen/mednafen.h" is required
|
|
// for #include "mednafen/psx/gpu.h" to work properly.
|
|
#include "mednafen/mednafen.h"
|
|
#include "mednafen/psx/gpu.h"
|
|
|
|
#include "libretro_cbs.h"
|
|
#include "libretro_options.h"
|
|
|
|
using namespace Vulkan;
|
|
using namespace PSX;
|
|
using namespace std;
|
|
|
|
static Context *context;
|
|
static Device *device;
|
|
static Renderer *renderer;
|
|
static unsigned scaling = 4;
|
|
|
|
// Declare extern as workaround for now to avoid variable
|
|
// naming conflicts with beetle_psx_globals.h
|
|
extern "C" uint8_t widescreen_hack;
|
|
extern "C" uint8_t widescreen_hack_aspect_ratio_setting;
|
|
extern "C" bool content_is_pal;
|
|
extern "C" int filter_mode;
|
|
extern "C" bool currently_interlaced;
|
|
extern "C" int aspect_ratio_setting;
|
|
|
|
extern retro_log_printf_t log_cb;
|
|
namespace Granite
|
|
{
|
|
retro_log_printf_t libretro_log;
|
|
}
|
|
|
|
static retro_hw_render_callback hw_render;
|
|
static const struct retro_hw_render_interface_vulkan *vulkan;
|
|
static retro_vulkan_image swapchain_image;
|
|
static Renderer::SaveState save_state;
|
|
static bool inside_frame;
|
|
static bool has_software_fb;
|
|
static bool scaled_uv_offset;
|
|
static int filter_exclude_sprites;
|
|
static int filter_exclude_2d_polygons;
|
|
static bool adaptive_smoothing;
|
|
static bool super_sampling;
|
|
static unsigned msaa = 1;
|
|
static bool mdec_yuv;
|
|
static vector<function<void ()>> defer;
|
|
static dither_mode dither_mode = DITHER_NATIVE;
|
|
static bool dump_textures = false;
|
|
static bool replace_textures = false;
|
|
static bool track_textures = false;
|
|
static int crop_overscan;
|
|
static int image_offset_cycles;
|
|
static unsigned image_crop;
|
|
static int initial_scanline;
|
|
static int last_scanline;
|
|
static int initial_scanline_pal;
|
|
static int last_scanline_pal;
|
|
static bool frame_duping_enabled = false;
|
|
static uint32_t prev_frame_width = 320;
|
|
static uint32_t prev_frame_height = 240;
|
|
static bool show_vram = false;
|
|
|
|
static retro_video_refresh_t video_refresh_cb;
|
|
|
|
static const VkApplicationInfo *get_application_info(void)
|
|
{
|
|
static const VkApplicationInfo info = {
|
|
VK_STRUCTURE_TYPE_APPLICATION_INFO,
|
|
nullptr,
|
|
"Beetle PSX",
|
|
0,
|
|
"parallel-psx",
|
|
0,
|
|
VK_MAKE_VERSION(1, 0, 32),
|
|
};
|
|
return &info;
|
|
}
|
|
|
|
static void vk_context_reset(void)
|
|
{
|
|
if (!environ_cb(RETRO_ENVIRONMENT_GET_HW_RENDER_INTERFACE, (void**)&vulkan) || !vulkan)
|
|
return;
|
|
|
|
if (vulkan->interface_version != RETRO_HW_RENDER_INTERFACE_VULKAN_VERSION)
|
|
{
|
|
vulkan = nullptr;
|
|
return;
|
|
}
|
|
|
|
assert(context);
|
|
device = new Device;
|
|
device->set_context(*context);
|
|
|
|
renderer = new Renderer(*device, scaling, msaa, save_state.vram.empty() ? nullptr : &save_state);
|
|
|
|
for (auto &func : defer)
|
|
func();
|
|
defer.clear();
|
|
|
|
renderer->flush();
|
|
}
|
|
|
|
static void vk_context_destroy(void)
|
|
{
|
|
save_state = renderer->save_vram_state();
|
|
vulkan = nullptr;
|
|
|
|
delete renderer;
|
|
delete device;
|
|
delete context;
|
|
renderer = nullptr;
|
|
device = nullptr;
|
|
context = nullptr;
|
|
}
|
|
|
|
static bool libretro_create_device(
|
|
struct retro_vulkan_context *libretro_context,
|
|
VkInstance instance,
|
|
VkPhysicalDevice gpu,
|
|
VkSurfaceKHR surface,
|
|
PFN_vkGetInstanceProcAddr get_instance_proc_addr,
|
|
const char **required_device_extensions,
|
|
unsigned num_required_device_extensions,
|
|
const char **required_device_layers,
|
|
unsigned num_required_device_layers,
|
|
const VkPhysicalDeviceFeatures *required_features)
|
|
{
|
|
if (!Vulkan::Context::init_loader(get_instance_proc_addr))
|
|
return false;
|
|
|
|
if (context)
|
|
{
|
|
delete context;
|
|
context = nullptr;
|
|
}
|
|
|
|
try
|
|
{
|
|
context = new Vulkan::Context(instance, gpu, surface, required_device_extensions, num_required_device_extensions,
|
|
required_device_layers, num_required_device_layers,
|
|
required_features);
|
|
}
|
|
catch (const std::exception &)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
context->set_notification_callback([](const char* message) {
|
|
printf("Vulkan Validation Layer Says: %s\n", message);
|
|
});
|
|
context->release_device();
|
|
libretro_context->gpu = context->get_gpu();
|
|
libretro_context->device = context->get_device();
|
|
libretro_context->presentation_queue = context->get_graphics_queue();
|
|
libretro_context->presentation_queue_family_index = context->get_graphics_queue_family();
|
|
libretro_context->queue = context->get_graphics_queue();
|
|
libretro_context->queue_family_index = context->get_graphics_queue_family();
|
|
return true;
|
|
}
|
|
|
|
bool rsx_vulkan_open(bool is_pal)
|
|
{
|
|
Granite::libretro_log = log_cb;
|
|
content_is_pal = is_pal;
|
|
|
|
hw_render.context_type = RETRO_HW_CONTEXT_VULKAN;
|
|
hw_render.version_major = VK_MAKE_VERSION(1, 0, 32);
|
|
hw_render.version_minor = 0;
|
|
hw_render.context_reset = vk_context_reset;
|
|
hw_render.context_destroy = vk_context_destroy;
|
|
hw_render.cache_context = false;
|
|
if (!environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER, &hw_render))
|
|
return false;
|
|
|
|
static const struct retro_hw_render_context_negotiation_interface_vulkan iface = {
|
|
RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN,
|
|
RETRO_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE_VULKAN_VERSION,
|
|
|
|
get_application_info,
|
|
libretro_create_device,
|
|
nullptr,
|
|
};
|
|
|
|
environ_cb(RETRO_ENVIRONMENT_SET_HW_RENDER_CONTEXT_NEGOTIATION_INTERFACE, (void*)&iface);
|
|
|
|
return true;
|
|
}
|
|
|
|
void rsx_vulkan_set_environment(retro_environment_t cb)
|
|
{
|
|
environ_cb = cb;
|
|
}
|
|
|
|
void rsx_vulkan_set_video_refresh(retro_video_refresh_t cb)
|
|
{
|
|
video_refresh_cb = cb;
|
|
}
|
|
|
|
void rsx_vulkan_get_system_av_info(struct retro_system_av_info *info)
|
|
{
|
|
rsx_vulkan_refresh_variables();
|
|
|
|
memset(info, 0, sizeof(*info));
|
|
|
|
// Set retro_game_geometry
|
|
info->geometry.base_width = MEDNAFEN_CORE_GEOMETRY_BASE_W;
|
|
info->geometry.base_height = MEDNAFEN_CORE_GEOMETRY_BASE_H;
|
|
info->geometry.max_width = MEDNAFEN_CORE_GEOMETRY_MAX_W * (super_sampling ? 1 : scaling);
|
|
info->geometry.max_height = MEDNAFEN_CORE_GEOMETRY_MAX_H * (super_sampling ? 1 : scaling);
|
|
info->geometry.aspect_ratio = rsx_common_get_aspect_ratio(content_is_pal, crop_overscan,
|
|
content_is_pal ? initial_scanline_pal : initial_scanline,
|
|
content_is_pal ? last_scanline_pal : last_scanline,
|
|
aspect_ratio_setting, show_vram, widescreen_hack, widescreen_hack_aspect_ratio_setting);
|
|
|
|
// Set retro_system_timing
|
|
info->timing.fps = rsx_common_get_timing_fps();
|
|
info->timing.sample_rate = SOUND_FREQUENCY;
|
|
}
|
|
|
|
void rsx_vulkan_refresh_variables(void)
|
|
{
|
|
struct retro_variable var = {0};
|
|
|
|
var.key = BEETLE_OPT(renderer_software_fb);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "enabled"))
|
|
has_software_fb = true;
|
|
else
|
|
has_software_fb = false;
|
|
}
|
|
else
|
|
/* If 'BEETLE_OPT(renderer_software_fb)' option is not found, then
|
|
* we are running in software mode */
|
|
has_software_fb = true;
|
|
|
|
unsigned old_scaling = scaling;
|
|
unsigned old_msaa = msaa;
|
|
bool old_super_sampling = super_sampling;
|
|
bool old_show_vram = show_vram;
|
|
int old_crop_overscan = crop_overscan;
|
|
unsigned old_image_crop = image_crop;
|
|
bool old_widescreen_hack = widescreen_hack;
|
|
unsigned old_widescreen_hack_aspect_ratio_setting = widescreen_hack_aspect_ratio_setting;
|
|
bool visible_scanlines_changed = false;
|
|
|
|
var.key = BEETLE_OPT(internal_resolution);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
/* Same limitations as libretro.cpp */
|
|
scaling = var.value[0] - '0';
|
|
if (var.value[1] != 'x')
|
|
{
|
|
scaling = (var.value[0] - '0') * 10;
|
|
scaling += var.value[1] - '0';
|
|
}
|
|
}
|
|
|
|
var.key = BEETLE_OPT(scaled_uv_offset);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "enabled"))
|
|
scaled_uv_offset = true;
|
|
else
|
|
scaled_uv_offset = false;
|
|
}
|
|
|
|
var.key = BEETLE_OPT(filter_exclude_sprite);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "all"))
|
|
filter_exclude_sprites = 2;
|
|
else if (!strcmp(var.value, "opaque"))
|
|
filter_exclude_sprites = 1;
|
|
else
|
|
filter_exclude_sprites = 0;
|
|
}
|
|
|
|
var.key = BEETLE_OPT(filter_exclude_2d_polygon);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "all"))
|
|
filter_exclude_2d_polygons = 2;
|
|
else if (!strcmp(var.value, "opaque"))
|
|
filter_exclude_2d_polygons = 1;
|
|
else
|
|
filter_exclude_2d_polygons = 0;
|
|
}
|
|
|
|
var.key = BEETLE_OPT(adaptive_smoothing);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "enabled"))
|
|
adaptive_smoothing = true;
|
|
else
|
|
adaptive_smoothing = false;
|
|
}
|
|
|
|
var.key = BEETLE_OPT(super_sampling);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "enabled"))
|
|
super_sampling = true;
|
|
else
|
|
super_sampling = false;
|
|
}
|
|
|
|
var.key = BEETLE_OPT(msaa);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
msaa = strtoul(var.value, nullptr, 0);
|
|
}
|
|
|
|
var.key = BEETLE_OPT(mdec_yuv);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "enabled"))
|
|
mdec_yuv = true;
|
|
else
|
|
mdec_yuv = false;
|
|
}
|
|
|
|
var.key = BEETLE_OPT(dither_mode);
|
|
dither_mode = DITHER_NATIVE;
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "internal resolution"))
|
|
dither_mode = DITHER_UPSCALED;
|
|
else if (!strcmp(var.value, "disabled"))
|
|
dither_mode = DITHER_OFF;
|
|
}
|
|
|
|
var.key = BEETLE_OPT(crop_overscan);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (strcmp(var.value, "disabled") == 0)
|
|
crop_overscan = 0;
|
|
else if (strcmp(var.value, "static") == 0)
|
|
crop_overscan = 1;
|
|
else if (strcmp(var.value, "smart") == 0)
|
|
crop_overscan = 2;
|
|
}
|
|
|
|
var.key = BEETLE_OPT(image_offset_cycles);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
image_offset_cycles = atoi(var.value);
|
|
}
|
|
|
|
var.key = BEETLE_OPT(image_crop);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (strcmp(var.value, "disabled") == 0)
|
|
image_crop = 0;
|
|
else
|
|
image_crop = atoi(var.value);
|
|
}
|
|
|
|
var.key = BEETLE_OPT(initial_scanline);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
int new_initial_scanline = atoi(var.value);
|
|
if (new_initial_scanline != initial_scanline)
|
|
{
|
|
initial_scanline = new_initial_scanline;
|
|
visible_scanlines_changed = true;
|
|
}
|
|
}
|
|
|
|
var.key = BEETLE_OPT(last_scanline);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
int new_last_scanline = atoi(var.value);
|
|
if (new_last_scanline != last_scanline)
|
|
{
|
|
last_scanline = new_last_scanline;
|
|
visible_scanlines_changed = true;
|
|
}
|
|
}
|
|
|
|
var.key = BEETLE_OPT(initial_scanline_pal);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
int new_initial_scanline_pal = atoi(var.value);
|
|
if (new_initial_scanline_pal != initial_scanline_pal)
|
|
{
|
|
initial_scanline_pal = new_initial_scanline_pal;
|
|
visible_scanlines_changed = true;
|
|
}
|
|
}
|
|
|
|
var.key = BEETLE_OPT(last_scanline_pal);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
int new_last_scanline_pal = atoi(var.value);
|
|
if (new_last_scanline_pal != last_scanline_pal)
|
|
{
|
|
last_scanline_pal = new_last_scanline_pal;
|
|
visible_scanlines_changed = true;
|
|
}
|
|
}
|
|
|
|
var.key = BEETLE_OPT(widescreen_hack);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "enabled"))
|
|
widescreen_hack = true;
|
|
else
|
|
widescreen_hack = false;
|
|
}
|
|
|
|
var.key = BEETLE_OPT(widescreen_hack_aspect_ratio);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "16:10"))
|
|
widescreen_hack_aspect_ratio_setting = 0;
|
|
else if (!strcmp(var.value, "16:9"))
|
|
widescreen_hack_aspect_ratio_setting = 1;
|
|
else if (!strcmp(var.value, "18:9"))
|
|
widescreen_hack_aspect_ratio_setting = 2;
|
|
else if (!strcmp(var.value, "19:9"))
|
|
widescreen_hack_aspect_ratio_setting = 3;
|
|
else if (!strcmp(var.value, "20:9"))
|
|
widescreen_hack_aspect_ratio_setting = 4;
|
|
else if (!strcmp(var.value, "21:9"))
|
|
widescreen_hack_aspect_ratio_setting = 5;
|
|
else if (!strcmp(var.value, "32:9"))
|
|
widescreen_hack_aspect_ratio_setting = 6;
|
|
}
|
|
|
|
var.key = BEETLE_OPT(track_textures);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "enabled"))
|
|
track_textures = true;
|
|
else
|
|
track_textures = false;
|
|
}
|
|
|
|
var.key = BEETLE_OPT(dump_textures);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "enabled"))
|
|
dump_textures = true;
|
|
else
|
|
dump_textures = false;
|
|
}
|
|
|
|
var.key = BEETLE_OPT(replace_textures);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "enabled"))
|
|
replace_textures = true;
|
|
else
|
|
replace_textures = false;
|
|
}
|
|
|
|
struct retro_core_option_display option_display;
|
|
option_display.visible = track_textures;
|
|
|
|
option_display.key = BEETLE_OPT(dump_textures);
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
option_display.key = BEETLE_OPT(replace_textures);
|
|
environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display);
|
|
|
|
var.key = BEETLE_OPT(frame_duping);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "enabled"))
|
|
{
|
|
bool frontend_can_dupe = false;
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_CAN_DUPE, &frontend_can_dupe))
|
|
{
|
|
frame_duping_enabled = frontend_can_dupe;
|
|
if (!frontend_can_dupe)
|
|
log_cb(RETRO_LOG_INFO, "Frontend does not support frame duping. Frame duping will be disabled.\n");
|
|
}
|
|
}
|
|
else
|
|
frame_duping_enabled = false;
|
|
}
|
|
|
|
var.key = BEETLE_OPT(display_vram);
|
|
if (environ_cb(RETRO_ENVIRONMENT_GET_VARIABLE, &var) && var.value)
|
|
{
|
|
if (!strcmp(var.value, "enabled"))
|
|
show_vram = true;
|
|
else
|
|
show_vram = false;
|
|
}
|
|
|
|
// Clean this up. Possible to categorize by order of severity, e.g. geometry dirty flag vs full system_av dirty flag
|
|
if ((old_scaling != scaling ||
|
|
old_super_sampling != super_sampling ||
|
|
old_msaa != msaa ||
|
|
old_show_vram != show_vram ||
|
|
old_crop_overscan != crop_overscan ||
|
|
old_image_crop != image_crop ||
|
|
old_widescreen_hack != widescreen_hack ||
|
|
old_widescreen_hack_aspect_ratio_setting != widescreen_hack_aspect_ratio_setting ||
|
|
visible_scanlines_changed)
|
|
&& renderer)
|
|
{
|
|
// Potential bad behavior from calling rsx_vulkan_get_system_av_info() from inside
|
|
// rsx_vulkan_refresh_variables() since both functions call each other...
|
|
retro_system_av_info info;
|
|
rsx_vulkan_get_system_av_info(&info);
|
|
|
|
if (!environ_cb(RETRO_ENVIRONMENT_SET_SYSTEM_AV_INFO, &info))
|
|
{
|
|
// Failed to change scale, just keep using the old one.
|
|
scaling = old_scaling;
|
|
}
|
|
}
|
|
}
|
|
|
|
void rsx_vulkan_prepare_frame(void)
|
|
{
|
|
inside_frame = true;
|
|
device->flush_frame();
|
|
vulkan->wait_sync_index(vulkan->handle);
|
|
unsigned index = vulkan->get_sync_index(vulkan->handle);
|
|
device->next_frame_context();
|
|
renderer->reset_counters();
|
|
|
|
renderer->set_scaled_uv_offset(scaled_uv_offset);
|
|
renderer->set_filter_mode(static_cast<Renderer::FilterMode>(filter_mode));
|
|
renderer->set_sprite_filter_exclude(static_cast<Renderer::FilterExclude>(filter_exclude_sprites));
|
|
renderer->set_polygon_2d_filter_exclude(static_cast<Renderer::FilterExclude>(filter_exclude_2d_polygons));
|
|
}
|
|
|
|
static Renderer::ScanoutMode get_scanout_mode(bool bpp24)
|
|
{
|
|
if (bpp24)
|
|
return Renderer::ScanoutMode::BGR24;
|
|
else if (dither_mode != DITHER_OFF)
|
|
return Renderer::ScanoutMode::ABGR1555_Dither;
|
|
else
|
|
return Renderer::ScanoutMode::ABGR1555_555;
|
|
}
|
|
|
|
void rsx_vulkan_finalize_frame(const void *fb, unsigned width,
|
|
unsigned height, unsigned pitch)
|
|
{
|
|
if (frame_duping_enabled && !GPU_get_display_change_count())
|
|
{
|
|
/* Any visual core option changes will be deferred to next non-duped frame */
|
|
|
|
//printf("No PSX GPU display update; duping frame\n");
|
|
renderer->flush();
|
|
video_refresh_cb(NULL, prev_frame_width, prev_frame_height, 0);
|
|
|
|
inside_frame = false;
|
|
return;
|
|
}
|
|
|
|
renderer->set_track_textures(track_textures);
|
|
renderer->set_dump_textures(dump_textures);
|
|
renderer->set_replace_textures(replace_textures);
|
|
renderer->set_adaptive_smoothing(adaptive_smoothing);
|
|
renderer->set_dither_native_resolution(dither_mode == DITHER_NATIVE);
|
|
renderer->set_horizontal_overscan_cropping(crop_overscan);
|
|
renderer->set_horizontal_offset_cycles(image_offset_cycles);
|
|
renderer->set_visible_scanlines(initial_scanline, last_scanline, initial_scanline_pal, last_scanline_pal);
|
|
renderer->set_horizontal_additional_cropping(image_crop);
|
|
|
|
renderer->set_display_filter(super_sampling ? Renderer::ScanoutFilter::SSAA : Renderer::ScanoutFilter::None);
|
|
if (renderer->get_scanout_mode() == Renderer::ScanoutMode::BGR24)
|
|
renderer->set_mdec_filter(mdec_yuv ? Renderer::ScanoutFilter::MDEC_YUV : Renderer::ScanoutFilter::None);
|
|
else
|
|
renderer->set_mdec_filter(Renderer::ScanoutFilter::None);
|
|
|
|
auto scanout = show_vram ? renderer->scanout_vram_to_texture() : renderer->scanout_to_texture();
|
|
|
|
retro_vulkan_image *image = &swapchain_image;
|
|
|
|
image->create_info.sType =
|
|
VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
|
image->create_info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
|
image->create_info.format = scanout->get_format();
|
|
image->create_info.subresourceRange.baseMipLevel = 0;
|
|
image->create_info.subresourceRange.baseArrayLayer = 0;
|
|
image->create_info.subresourceRange.levelCount = 1;
|
|
image->create_info.subresourceRange.layerCount = 1;
|
|
image->create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
image->create_info.components.r = VK_COMPONENT_SWIZZLE_R;
|
|
image->create_info.components.g = VK_COMPONENT_SWIZZLE_G;
|
|
image->create_info.components.b = VK_COMPONENT_SWIZZLE_B;
|
|
image->create_info.components.a = VK_COMPONENT_SWIZZLE_A;
|
|
image->create_info.image = scanout->get_image();
|
|
image->image_layout =
|
|
VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
|
image->image_view =
|
|
scanout->get_view().get_view();
|
|
|
|
vulkan->set_image(vulkan->handle, image, 0,
|
|
nullptr, VK_QUEUE_FAMILY_IGNORED);
|
|
renderer->flush();
|
|
|
|
auto semaphore = device->request_semaphore();
|
|
vulkan->set_signal_semaphore(vulkan->handle, semaphore->get_semaphore());
|
|
semaphore->signal_external();
|
|
renderer->set_scanout_semaphore(semaphore);
|
|
video_refresh_cb(RETRO_HW_FRAME_BUFFER_VALID, scanout->get_width(), scanout->get_height(), 0);
|
|
inside_frame = false;
|
|
|
|
prev_frame_width = scanout->get_width();
|
|
prev_frame_height = scanout ->get_height();
|
|
|
|
#if 0
|
|
printf("%d %d\n", scanout->get_width(), scanout->get_height());
|
|
|
|
fprintf(stderr, "Render passes: %u, Readback: %u, Writeout: %u\n",
|
|
renderer->counters.render_passes, renderer->counters.fragment_readback_pixels, renderer->counters.fragment_writeout_pixels);
|
|
#endif
|
|
}
|
|
|
|
/* Draw commands */
|
|
|
|
void rsx_vulkan_set_tex_window(uint8_t tww, uint8_t twh,
|
|
uint8_t twx, uint8_t twy)
|
|
{
|
|
uint8_t tex_x_mask = ~(tww << 3);
|
|
uint8_t tex_y_mask = ~(twh << 3);
|
|
uint8_t tex_x_or = (twx & tww) << 3;
|
|
uint8_t tex_y_or = (twy & twh) << 3;
|
|
|
|
if (renderer)
|
|
renderer->set_texture_window({ tex_x_mask, tex_y_mask, tex_x_or, tex_y_or });
|
|
else
|
|
{
|
|
defer.push_back([=]() {
|
|
renderer->set_texture_window({ tex_x_mask, tex_y_mask, tex_x_or, tex_y_or});
|
|
});
|
|
}
|
|
}
|
|
|
|
void rsx_vulkan_set_draw_offset(int16_t x, int16_t y)
|
|
{
|
|
if (renderer)
|
|
renderer->set_draw_offset(x, y);
|
|
else
|
|
{
|
|
defer.push_back([=]() {
|
|
renderer->set_draw_offset(x, y);
|
|
});
|
|
}
|
|
}
|
|
|
|
void rsx_vulkan_set_draw_area(uint16_t x0, uint16_t y0,
|
|
uint16_t x1, uint16_t y1)
|
|
{
|
|
int width = x1 - x0 + 1;
|
|
int height = y1 - y0 + 1;
|
|
width = max(width, 0);
|
|
height = max(height, 0);
|
|
|
|
width = min(width, int(FB_WIDTH - x0));
|
|
height = min(height, int(FB_HEIGHT - y0));
|
|
|
|
if (renderer)
|
|
renderer->set_draw_rect({ x0, y0, unsigned(width), unsigned(height) });
|
|
else
|
|
{
|
|
defer.push_back([=]() {
|
|
renderer->set_draw_rect({ x0, y0, unsigned(width), unsigned(height) });
|
|
});
|
|
}
|
|
}
|
|
|
|
void rsx_vulkan_set_vram_framebuffer_coords(uint32_t xstart, uint32_t ystart)
|
|
{
|
|
if (renderer)
|
|
renderer->set_vram_framebuffer_coords(xstart, ystart);
|
|
else
|
|
{
|
|
defer.push_back([=]() {
|
|
renderer->set_vram_framebuffer_coords(xstart, ystart);
|
|
});
|
|
}
|
|
}
|
|
|
|
void rsx_vulkan_set_horizontal_display_range(uint16_t x1, uint16_t x2)
|
|
{
|
|
if (renderer)
|
|
renderer->set_horizontal_display_range(x1, x2);
|
|
else
|
|
{
|
|
defer.push_back([=]() {
|
|
renderer->set_horizontal_display_range(x1, x2);
|
|
});
|
|
}
|
|
}
|
|
|
|
void rsx_vulkan_set_vertical_display_range(uint16_t y1, uint16_t y2)
|
|
{
|
|
if (renderer)
|
|
renderer->set_vertical_display_range(y1, y2);
|
|
else
|
|
{
|
|
defer.push_back([=]() {
|
|
renderer->set_vertical_display_range(y1, y2);
|
|
});
|
|
}
|
|
}
|
|
|
|
void rsx_vulkan_set_display_mode(bool depth_24bpp,
|
|
bool is_pal,
|
|
bool is_480i,
|
|
int width_mode)
|
|
{
|
|
if (renderer)
|
|
renderer->set_display_mode(get_scanout_mode(depth_24bpp), is_pal,
|
|
is_480i, static_cast<Renderer::WidthMode>(width_mode));
|
|
else
|
|
{
|
|
defer.push_back([=]() {
|
|
renderer->set_display_mode(get_scanout_mode(depth_24bpp), is_pal,
|
|
is_480i, static_cast<Renderer::WidthMode>(width_mode));
|
|
});
|
|
}
|
|
}
|
|
|
|
void rsx_vulkan_push_triangle(
|
|
float p0x, float p0y, float p0w,
|
|
float p1x, float p1y, float p1w,
|
|
float p2x, float p2y, float p2w,
|
|
uint32_t c0,
|
|
uint32_t c1,
|
|
uint32_t c2,
|
|
uint16_t t0x, uint16_t t0y,
|
|
uint16_t t1x, uint16_t t1y,
|
|
uint16_t t2x, uint16_t t2y,
|
|
uint16_t min_u, uint16_t min_v,
|
|
uint16_t max_u, uint16_t max_v,
|
|
uint16_t texpage_x, uint16_t texpage_y,
|
|
uint16_t clut_x, uint16_t clut_y,
|
|
uint8_t texture_blend_mode,
|
|
uint8_t depth_shift,
|
|
bool dither,
|
|
int blend_mode,
|
|
bool mask_test, bool set_mask)
|
|
{
|
|
if (!renderer)
|
|
return;
|
|
|
|
renderer->set_texture_color_modulate(texture_blend_mode == 2);
|
|
renderer->set_palette_offset(clut_x, clut_y);
|
|
renderer->set_texture_offset(texpage_x, texpage_y);
|
|
//renderer->set_dither(dither);
|
|
renderer->set_mask_test(mask_test);
|
|
renderer->set_force_mask_bit(set_mask);
|
|
renderer->set_UV_limits(min_u, min_v, max_u, max_v);
|
|
if (texture_blend_mode != 0)
|
|
{
|
|
switch (depth_shift)
|
|
{
|
|
default:
|
|
case 0:
|
|
renderer->set_texture_mode(TextureMode::ABGR1555);
|
|
break;
|
|
case 1:
|
|
renderer->set_texture_mode(TextureMode::Palette8bpp);
|
|
break;
|
|
case 2:
|
|
renderer->set_texture_mode(TextureMode::Palette4bpp);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
renderer->set_texture_mode(TextureMode::None);
|
|
|
|
switch (blend_mode)
|
|
{
|
|
default:
|
|
renderer->set_semi_transparent(SemiTransparentMode::None);
|
|
break;
|
|
|
|
case 0:
|
|
renderer->set_semi_transparent(SemiTransparentMode::Average);
|
|
break;
|
|
case 1:
|
|
renderer->set_semi_transparent(SemiTransparentMode::Add);
|
|
break;
|
|
case 2:
|
|
renderer->set_semi_transparent(SemiTransparentMode::Sub);
|
|
break;
|
|
case 3:
|
|
renderer->set_semi_transparent(SemiTransparentMode::AddQuarter);
|
|
break;
|
|
}
|
|
|
|
renderer->set_primitive_type(PrimitiveType::Polygon);
|
|
|
|
Vertex vertices[3] = {
|
|
{ p0x, p0y, p0w, c0, t0x, t0y },
|
|
{ p1x, p1y, p1w, c1, t1x, t1y },
|
|
{ p2x, p2y, p2w, c2, t2x, t2y },
|
|
};
|
|
|
|
renderer->draw_triangle(vertices);
|
|
}
|
|
|
|
void rsx_vulkan_push_quad(
|
|
float p0x, float p0y, float p0w,
|
|
float p1x, float p1y, float p1w,
|
|
float p2x, float p2y, float p2w,
|
|
float p3x, float p3y, float p3w,
|
|
uint32_t c0, uint32_t c1, uint32_t c2, uint32_t c3,
|
|
uint16_t t0x, uint16_t t0y,
|
|
uint16_t t1x, uint16_t t1y,
|
|
uint16_t t2x, uint16_t t2y,
|
|
uint16_t t3x, uint16_t t3y,
|
|
uint16_t min_u, uint16_t min_v,
|
|
uint16_t max_u, uint16_t max_v,
|
|
uint16_t texpage_x, uint16_t texpage_y,
|
|
uint16_t clut_x, uint16_t clut_y,
|
|
uint8_t texture_blend_mode,
|
|
uint8_t depth_shift,
|
|
bool dither,
|
|
int blend_mode,
|
|
bool mask_test, bool set_mask,
|
|
bool is_sprite, bool may_be_2d)
|
|
{
|
|
if (!renderer)
|
|
return;
|
|
|
|
renderer->set_texture_color_modulate(texture_blend_mode == 2);
|
|
renderer->set_palette_offset(clut_x, clut_y);
|
|
renderer->set_texture_offset(texpage_x, texpage_y);
|
|
//renderer->set_dither(dither);
|
|
renderer->set_mask_test(mask_test);
|
|
renderer->set_force_mask_bit(set_mask);
|
|
renderer->set_UV_limits(min_u, min_v, max_u, max_v);
|
|
if (texture_blend_mode != 0)
|
|
{
|
|
switch (depth_shift)
|
|
{
|
|
default:
|
|
case 0:
|
|
renderer->set_texture_mode(TextureMode::ABGR1555);
|
|
break;
|
|
case 1:
|
|
renderer->set_texture_mode(TextureMode::Palette8bpp);
|
|
break;
|
|
case 2:
|
|
renderer->set_texture_mode(TextureMode::Palette4bpp);
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
renderer->set_texture_mode(TextureMode::None);
|
|
|
|
switch (blend_mode)
|
|
{
|
|
default:
|
|
renderer->set_semi_transparent(SemiTransparentMode::None);
|
|
break;
|
|
|
|
case 0:
|
|
renderer->set_semi_transparent(SemiTransparentMode::Average);
|
|
break;
|
|
case 1:
|
|
renderer->set_semi_transparent(SemiTransparentMode::Add);
|
|
break;
|
|
case 2:
|
|
renderer->set_semi_transparent(SemiTransparentMode::Sub);
|
|
break;
|
|
case 3:
|
|
renderer->set_semi_transparent(SemiTransparentMode::AddQuarter);
|
|
break;
|
|
}
|
|
|
|
if (is_sprite)
|
|
renderer->set_primitive_type(PrimitiveType::Sprite);
|
|
else if (may_be_2d)
|
|
renderer->set_primitive_type(PrimitiveType::May_Be_2D_Polygon);
|
|
else
|
|
renderer->set_primitive_type(PrimitiveType::Polygon);
|
|
|
|
Vertex vertices[4] = {
|
|
{ p0x, p0y, p0w, c0, t0x, t0y },
|
|
{ p1x, p1y, p1w, c1, t1x, t1y },
|
|
{ p2x, p2y, p2w, c2, t2x, t2y },
|
|
{ p3x, p3y, p3w, c3, t3x, t3y },
|
|
};
|
|
|
|
renderer->draw_quad(vertices);
|
|
}
|
|
|
|
void rsx_vulkan_push_line(
|
|
int16_t p0x, int16_t p0y,
|
|
int16_t p1x, int16_t p1y,
|
|
uint32_t c0,
|
|
uint32_t c1,
|
|
bool dither,
|
|
int blend_mode,
|
|
bool mask_test, bool set_mask)
|
|
{
|
|
if (!renderer)
|
|
return;
|
|
|
|
renderer->set_texture_mode(TextureMode::None);
|
|
renderer->set_mask_test(mask_test);
|
|
renderer->set_force_mask_bit(set_mask);
|
|
switch (blend_mode)
|
|
{
|
|
default:
|
|
renderer->set_semi_transparent(SemiTransparentMode::None);
|
|
break;
|
|
|
|
case 0:
|
|
renderer->set_semi_transparent(SemiTransparentMode::Average);
|
|
break;
|
|
case 1:
|
|
renderer->set_semi_transparent(SemiTransparentMode::Add);
|
|
break;
|
|
case 2:
|
|
renderer->set_semi_transparent(SemiTransparentMode::Sub);
|
|
break;
|
|
case 3:
|
|
renderer->set_semi_transparent(SemiTransparentMode::AddQuarter);
|
|
break;
|
|
}
|
|
|
|
Vertex vertices[2] = {
|
|
{ float(p0x), float(p0y), 1.0f, c0, 0, 0 },
|
|
{ float(p1x), float(p1y), 1.0f, c1, 0, 0 },
|
|
};
|
|
//renderer->set_dither(dither);
|
|
renderer->set_texture_color_modulate(false);
|
|
renderer->draw_line(vertices);
|
|
}
|
|
|
|
void rsx_vulkan_load_image(
|
|
uint16_t x, uint16_t y,
|
|
uint16_t w, uint16_t h,
|
|
uint16_t *vram,
|
|
bool mask_test, bool set_mask)
|
|
{
|
|
#ifndef NDEBUG
|
|
TT_LOG_VERBOSE(RETRO_LOG_INFO, "rsx_vulkan_load_image(x=%i, y=%i, w=%i, h=%i, mask_test=%i, set_mask=%i).\n", x, y, w, h, mask_test, set_mask);
|
|
#endif
|
|
if (!renderer)
|
|
{
|
|
// Generally happens if someone loads a save state before the Vulkan context is created.
|
|
defer.push_back([=]() {
|
|
rsx_vulkan_load_image(x, y, w, h, vram, mask_test, set_mask);
|
|
});
|
|
return;
|
|
}
|
|
|
|
renderer->notify_texture_upload(PSX::Rect { x, y, w, h }, vram);
|
|
bool dual_copy = x + w > FB_WIDTH; // Check if we need to handle wrap-around in X.
|
|
renderer->set_mask_test(mask_test);
|
|
renderer->set_force_mask_bit(set_mask);
|
|
auto handle = renderer->copy_cpu_to_vram({ x, y, w, h });
|
|
uint16_t *tmp = renderer->begin_copy(handle);
|
|
for (unsigned off_y = 0; off_y < h; off_y++)
|
|
{
|
|
if (dual_copy)
|
|
{
|
|
unsigned first = FB_WIDTH - x;
|
|
unsigned second = w - first;
|
|
memcpy(tmp + off_y * w, vram + ((y + off_y) & (FB_HEIGHT - 1)) * FB_WIDTH + x, first * sizeof(uint16_t));
|
|
memcpy(tmp + off_y * w + first,
|
|
vram + ((y + off_y) & (FB_HEIGHT - 1)) * FB_WIDTH,
|
|
second * sizeof(uint16_t));
|
|
}
|
|
else
|
|
{
|
|
memcpy(tmp + off_y * w,
|
|
vram + ((y + off_y) & (FB_HEIGHT - 1)) * FB_WIDTH + x,
|
|
w * sizeof(uint16_t));
|
|
}
|
|
}
|
|
renderer->end_copy(handle);
|
|
|
|
// This is called on state loading.
|
|
if (!inside_frame)
|
|
renderer->flush();
|
|
}
|
|
|
|
bool rsx_vulkan_read_vram(uint16_t x, uint16_t y,
|
|
uint16_t w, uint16_t h,
|
|
uint16_t *vram)
|
|
{
|
|
if (!renderer)
|
|
return false;
|
|
|
|
renderer->copy_vram_to_cpu_synchronous({ x, y, w, h }, vram);
|
|
return true;
|
|
}
|
|
|
|
void rsx_vulkan_fill_rect(uint32_t color,
|
|
uint16_t x, uint16_t y,
|
|
uint16_t w, uint16_t h)
|
|
{
|
|
if (renderer)
|
|
renderer->clear_rect({ x, y, w, h }, color);
|
|
}
|
|
|
|
void rsx_vulkan_copy_rect(uint16_t src_x, uint16_t src_y,
|
|
uint16_t dst_x, uint16_t dst_y,
|
|
uint16_t w, uint16_t h,
|
|
bool mask_test, bool set_mask)
|
|
{
|
|
if (!renderer)
|
|
return;
|
|
|
|
renderer->set_mask_test(mask_test);
|
|
renderer->set_force_mask_bit(set_mask);
|
|
renderer->blit_vram({ dst_x, dst_y, w, h }, { src_x, src_y, w, h });
|
|
}
|
|
|
|
void rsx_vulkan_toggle_display(bool status)
|
|
{
|
|
if (renderer)
|
|
renderer->toggle_display(status == 0);
|
|
else
|
|
{
|
|
defer.push_back([=] {
|
|
renderer->toggle_display(status == 0);
|
|
});
|
|
}
|
|
}
|
|
|
|
bool rsx_vulkan_has_software_renderer(void)
|
|
{
|
|
return has_software_fb;
|
|
}
|