n64-emu/third_party/parallel-rdp-standalone/parallel-rdp/video_interface.hpp
2023-08-06 14:03:29 +09:00

240 lines
7.8 KiB
C++

/* Copyright (c) 2020 Themaister
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
#pragma once
#include <stdint.h>
#include "device.hpp"
#include "rdp_common.hpp"
namespace RDP
{
struct ScanoutOptions
{
// Simple (obsolete) crop method. If crop_rect.enable is false, this
// crops top / bottom with number of pixels (doubled if interlace),
// and left / right are cropped in an aspect preserving way.
// If crop_rect.enable is true,
// this is ignored and the crop_rect struct is used instead.
// Crop pixels are adjusted for upscaling, pixels are assumed to
// be specified for the original resolution.
unsigned crop_overscan_pixels = 0;
struct CropRect
{
unsigned left = 0;
unsigned right = 0;
unsigned top = 0; // Doubled if interlace
unsigned bottom = 0; // Doubled if interlace
bool enable = false;
} crop_rect;
unsigned downscale_steps = 0;
// Works around certain game bugs. Considered a hack if enabled.
bool persist_frame_on_invalid_input = false;
// To be equivalent to reference behavior where
// pixels persist for an extra frame.
// Not hardware accurate, but needed for weave interlace mode.
bool blend_previous_frame = false;
// Upscale deinterlacing deinterlaces by upscaling in Y, with an Y coordinate offset matching the field.
// If disabled, weave interlacing is used.
// Weave deinterlacing should *not* be used, except to run test suite!
bool upscale_deinterlacing = true;
struct
{
bool aa = true;
bool scale = true;
bool serrate = true;
bool dither_filter = true;
bool divot_filter = true;
bool gamma_dither = true;
} vi;
// External memory support.
// If true, the scanout image will be created with external memory support.
// presist_frame_on_invalid_input must be false when using exports.
VkExternalMemoryHandleTypeFlagBits export_handle_type = {};
bool export_scanout = false;
};
struct VIScanoutBuffer
{
Vulkan::BufferHandle buffer;
Vulkan::Fence fence;
unsigned width = 0;
unsigned height = 0;
};
class Renderer;
class VideoInterface : public Vulkan::DebugChannelInterface
{
public:
void set_device(Vulkan::Device *device);
void set_renderer(Renderer *renderer);
void set_vi_register(VIRegister reg, uint32_t value);
void set_rdram(const Vulkan::Buffer *rdram, size_t offset, size_t size);
void set_hidden_rdram(const Vulkan::Buffer *hidden_rdram);
int resolve_shader_define(const char *name, const char *define) const;
Vulkan::ImageHandle scanout(VkImageLayout target_layout, const ScanoutOptions &options = {}, unsigned scale_factor = 1);
void scanout_memory_range(unsigned &offset, unsigned &length) const;
void set_shader_bank(const ShaderBank *bank);
enum PerScanlineRegisterBits
{
// Currently supported bits.
PER_SCANLINE_HSTART_BIT = 1 << 0,
PER_SCANLINE_XSCALE_BIT = 1 << 1
};
using PerScanlineRegisterFlags = uint32_t;
void begin_vi_register_per_scanline(PerScanlineRegisterFlags flags);
void set_vi_register_for_scanline(PerScanlineRegisterBits reg, uint32_t value);
void latch_vi_register_for_scanline(unsigned vi_line);
void end_vi_register_per_scanline();
private:
Vulkan::Device *device = nullptr;
Renderer *renderer = nullptr;
uint32_t vi_registers[unsigned(VIRegister::Count)] = {};
struct PerScanlineRegisterState
{
uint32_t latched_state;
uint32_t line_state[VI_V_END_MAX];
};
struct
{
PerScanlineRegisterState h_start;
PerScanlineRegisterState x_scale;
PerScanlineRegisterFlags flags = 0;
unsigned line = 0;
bool ended = false;
} per_line_state;
const Vulkan::Buffer *rdram = nullptr;
const Vulkan::Buffer *hidden_rdram = nullptr;
Vulkan::BufferHandle gamma_lut;
Vulkan::BufferViewHandle gamma_lut_view;
const ShaderBank *shader_bank = nullptr;
void init_gamma_table();
bool previous_frame_blank = false;
bool debug_channel = false;
int filter_debug_channel_x = -1;
int filter_debug_channel_y = -1;
void message(const std::string &tag, uint32_t code,
uint32_t x, uint32_t y, uint32_t z,
uint32_t num_words, const Vulkan::DebugChannelInterface::Word *words) override;
// Frame state.
uint32_t frame_count = 0;
uint32_t last_valid_frame_count = 0;
Vulkan::ImageHandle prev_scanout_image;
VkImageLayout prev_image_layout = VK_IMAGE_LAYOUT_UNDEFINED;
bool prev_image_is_external = false;
size_t rdram_offset = 0;
size_t rdram_size = 0;
bool timestamp = false;
struct HorizontalInfo
{
int32_t h_start;
int32_t h_start_clamp;
int32_t h_end_clamp;
int32_t x_start;
int32_t x_add;
int32_t y_start;
int32_t y_add;
int32_t y_base;
};
struct HorizontalInfoLines
{
HorizontalInfo lines[VI_MAX_OUTPUT_SCANLINES];
};
static void bind_horizontal_info_view(Vulkan::CommandBuffer &cmd, const HorizontalInfoLines &lines);
struct Registers
{
int vi_width;
int vi_offset;
int v_current_line;
bool is_pal;
uint32_t status;
int init_y_add;
// Global scale pass scissor box.
int h_start_clamp, h_res_clamp;
int h_start, h_res;
int v_start, v_res;
// For AA stages.
int max_x, max_y;
};
Registers decode_vi_registers(HorizontalInfoLines *lines) const;
void clear_per_scanline_state();
Vulkan::ImageHandle vram_fetch_stage(const Registers &registers,
unsigned scaling_factor) const;
Vulkan::ImageHandle aa_fetch_stage(Vulkan::CommandBuffer &cmd,
Vulkan::Image &vram_image,
const Registers &registers,
unsigned scaling_factor) const;
Vulkan::ImageHandle divot_stage(Vulkan::CommandBuffer &cmd,
Vulkan::Image &aa_image,
const Registers &registers,
unsigned scaling_factor) const;
Vulkan::ImageHandle scale_stage(Vulkan::CommandBuffer &cmd,
const Vulkan::Image *divot_image,
Registers registers,
const HorizontalInfoLines &lines,
unsigned scaling_factor,
bool degenerate,
const ScanoutOptions &options,
bool final_pass) const;
Vulkan::ImageHandle downscale_stage(Vulkan::CommandBuffer &cmd,
Vulkan::Image &scale_image,
unsigned scaling_factor,
unsigned downscale_factor,
const ScanoutOptions &options,
bool final_pass) const;
Vulkan::ImageHandle upscale_deinterlace(Vulkan::CommandBuffer &cmd,
Vulkan::Image &scale_image,
unsigned scaling_factor, bool field_select,
const ScanoutOptions &options) const;
static bool need_fetch_bug_emulation(const Registers &reg, unsigned scaling_factor);
};
}