nv2a: Add Vulkan renderer

This commit is contained in:
Matt Borgerson
2024-12-31 01:37:05 -07:00
committed by mborgerson
parent e639e0cdb7
commit a5385803db
114 changed files with 23348 additions and 10301 deletions
+2 -2
View File
@@ -71,8 +71,8 @@ IndentWidth: 4
AccessModifierOffset: -4
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
MacroBlockBegin: '.*_BEGIN$' # only PREC_BEGIN ?
MacroBlockEnd: '.*_END$'
#MacroBlockBegin: '.*_BEGIN$' # only PREC_BEGIN ?
#MacroBlockEnd: '.*_END$'
MaxEmptyLinesToKeep: 2
#PenaltyBreakBeforeFirstCallParameter: 19
#PenaltyBreakComment: 300
+11 -2
View File
@@ -82,9 +82,18 @@
[submodule "tomlplusplus"]
path = tomlplusplus
url = https://github.com/marzer/tomlplusplus
[submodule "hw/xbox/nv2a/thirdparty/nv2a_vsh_cpu"]
path = hw/xbox/nv2a/thirdparty/nv2a_vsh_cpu
[submodule "hw/xbox/nv2a/pgraph/thirdparty/nv2a_vsh_cpu"]
path = hw/xbox/nv2a/pgraph/thirdparty/nv2a_vsh_cpu
url = https://github.com/abaire/nv2a_vsh_cpu.git
[submodule "ui/thirdparty/httplib"]
path = ui/thirdparty/httplib
url = https://github.com/yhirose/cpp-httplib
[submodule "hw/xbox/nv2a/pgraph/vk/thirdparty/VulkanMemoryAllocator"]
path = thirdparty/VulkanMemoryAllocator
url = https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator
[submodule "thirdparty/volk"]
path = thirdparty/volk
url = https://github.com/zeux/volk
[submodule "thirdparty/SPIRV-Reflect"]
path = thirdparty/SPIRV-Reflect
url = https://github.com/KhronosGroup/SPIRV-Reflect
+6
View File
@@ -130,6 +130,12 @@ input:
default: 18 # w
display:
renderer:
type: enum
values: ["NULL", OPENGL, VULKAN]
default: OPENGL
vulkan:
validation_layers: bool
quality:
surface_scale:
type: integer
Vendored
+1 -1
View File
@@ -237,7 +237,7 @@ else
git_submodules_action="ignore"
fi
git_submodules="ui/keycodemapdb ui/thirdparty/imgui ui/thirdparty/implot ui/thirdparty/httplib util/xxHash tomlplusplus genconfig hw/xbox/nv2a/thirdparty/nv2a_vsh_cpu"
git_submodules="ui/keycodemapdb ui/thirdparty/imgui ui/thirdparty/implot ui/thirdparty/httplib util/xxHash tomlplusplus genconfig hw/xbox/nv2a/pgraph/thirdparty/nv2a_vsh_cpu thirdparty/volk thirdparty/VulkanMemoryAllocator thirdparty/SPIRV-Reflect"
git="git"
# Don't accept a target_list environment variable.
+3
View File
@@ -16,6 +16,9 @@ Build-Depends: debhelper (>= 11),
libssl-dev,
libpcap-dev,
libslirp-dev,
glslang-dev,
libvulkan-dev,
Standards-Version: 3.9.8
Homepage: https://xemu.app
XS-Debian-Vcs-Browser: https://github.com/mborgerson/xemu
+45 -53
View File
@@ -1,8 +1,9 @@
/*
* QEMU Geforce NV2A debug helpers
* QEMU Geforce NV2A profiling and debug helpers
*
* Copyright (c) 2015 Jannik Vogel
* Copyright (c) 2012 espes
* Copyright (c) 2015 Jannik Vogel
* Copyright (c) 2018-2023 Matt Borgerson
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
@@ -18,8 +19,8 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef HW_NV2A_DEBUG_H
#define HW_NV2A_DEBUG_H
#ifndef HW_XBOX_NV2A_DEBUG_H
#define HW_XBOX_NV2A_DEBUG_H
#include <stdint.h>
@@ -36,54 +37,6 @@
# define NV2A_DPRINTF(format, ...) do { } while (0)
#endif
// #define DEBUG_NV2A_GL
#ifdef DEBUG_NV2A_GL
#include <stdbool.h>
#include "gl/gloffscreen.h"
#include "config-host.h"
void gl_debug_initialize(void);
void gl_debug_message(bool cc, const char *fmt, ...);
void gl_debug_group_begin(const char *fmt, ...);
void gl_debug_group_end(void);
void gl_debug_label(GLenum target, GLuint name, const char *fmt, ...);
void gl_debug_frame_terminator(void);
# define NV2A_GL_DPRINTF(cc, format, ...) \
gl_debug_message(cc, "nv2a: " format, ## __VA_ARGS__)
# define NV2A_GL_DGROUP_BEGIN(format, ...) \
gl_debug_group_begin("nv2a: " format, ## __VA_ARGS__)
# define NV2A_GL_DGROUP_END() \
gl_debug_group_end()
# define NV2A_GL_DLABEL(target, name, format, ...) \
gl_debug_label(target, name, "nv2a: { " format " }", ## __VA_ARGS__)
#define NV2A_GL_DFRAME_TERMINATOR() \
gl_debug_frame_terminator()
#ifdef __cplusplus
extern "C" {
#endif
#ifdef CONFIG_RENDERDOC
bool nv2a_dbg_renderdoc_available(void);
void nv2a_dbg_renderdoc_capture_frames(uint32_t num_frames);
#endif
#ifdef __cplusplus
}
#endif
#else
# define NV2A_GL_DPRINTF(cc, format, ...) do { \
if (cc) NV2A_DPRINTF(format "\n", ##__VA_ARGS__ ); \
} while (0)
# define NV2A_GL_DGROUP_BEGIN(format, ...) do { } while (0)
# define NV2A_GL_DGROUP_END() do { } while (0)
# define NV2A_GL_DLABEL(target, name, format, ...) do { } while (0)
# define NV2A_GL_DFRAME_TERMINATOR() do { } while (0)
#endif
/* Debug prints to identify when unimplemented or unconfirmed features
* are being exercised. These cases likely result in graphical problems of
* varying degree, but should otherwise not crash the system. Enable this
@@ -111,6 +64,22 @@ void nv2a_dbg_renderdoc_capture_frames(uint32_t num_frames);
#endif
#define NV2A_PROF_COUNTERS_XMAC \
_X(NV2A_PROF_FINISH_VERTEX_BUFFER_DIRTY) \
_X(NV2A_PROF_FINISH_SURFACE_CREATE) \
_X(NV2A_PROF_FINISH_SURFACE_DOWN) \
_X(NV2A_PROF_FINISH_NEED_BUFFER_SPACE) \
_X(NV2A_PROF_FINISH_FRAMEBUFFER_DIRTY) \
_X(NV2A_PROF_FINISH_PRESENTING) \
_X(NV2A_PROF_FINISH_FLIP_STALL) \
_X(NV2A_PROF_FINISH_FLUSH) \
_X(NV2A_PROF_CLEAR) \
_X(NV2A_PROF_QUEUE_SUBMIT) \
_X(NV2A_PROF_QUEUE_SUBMIT_AUX) \
_X(NV2A_PROF_PIPELINE_NOTDIRTY) \
_X(NV2A_PROF_PIPELINE_GEN) \
_X(NV2A_PROF_PIPELINE_BIND) \
_X(NV2A_PROF_PIPELINE_MERGE) \
_X(NV2A_PROF_PIPELINE_RENDERPASSES) \
_X(NV2A_PROF_BEGIN_ENDS) \
_X(NV2A_PROF_DRAW_ARRAYS) \
_X(NV2A_PROF_INLINE_BUFFERS) \
@@ -120,18 +89,26 @@ void nv2a_dbg_renderdoc_capture_frames(uint32_t num_frames);
_X(NV2A_PROF_SHADER_GEN) \
_X(NV2A_PROF_SHADER_BIND) \
_X(NV2A_PROF_SHADER_BIND_NOTDIRTY) \
_X(NV2A_PROF_SHADER_UBO_DIRTY) \
_X(NV2A_PROF_SHADER_UBO_NOTDIRTY) \
_X(NV2A_PROF_ATTR_BIND) \
_X(NV2A_PROF_TEX_UPLOAD) \
_X(NV2A_PROF_TEX_BIND) \
_X(NV2A_PROF_GEOM_BUFFER_UPDATE_1) \
_X(NV2A_PROF_GEOM_BUFFER_UPDATE_2) \
_X(NV2A_PROF_GEOM_BUFFER_UPDATE_3) \
_X(NV2A_PROF_GEOM_BUFFER_UPDATE_4) \
_X(NV2A_PROF_GEOM_BUFFER_UPDATE_4_NOTDIRTY) \
_X(NV2A_PROF_SURF_SWIZZLE) \
_X(NV2A_PROF_SURF_CREATE) \
_X(NV2A_PROF_SURF_DOWNLOAD) \
_X(NV2A_PROF_SURF_UPLOAD) \
_X(NV2A_PROF_SURF_TO_TEX) \
_X(NV2A_PROF_SURF_TO_TEX_FALLBACK) \
_X(NV2A_PROF_QUEUE_SUBMIT_1) \
_X(NV2A_PROF_QUEUE_SUBMIT_2) \
_X(NV2A_PROF_QUEUE_SUBMIT_3) \
_X(NV2A_PROF_QUEUE_SUBMIT_4) \
_X(NV2A_PROF_QUEUE_SUBMIT_5) \
enum NV2A_PROF_COUNTERS_ENUM {
#define _X(x) x,
@@ -161,6 +138,21 @@ extern NV2AStats g_nv2a_stats;
const char *nv2a_profile_get_counter_name(unsigned int cnt);
int nv2a_profile_get_counter_value(unsigned int cnt);
void nv2a_profile_increment(void);
void nv2a_profile_flip_stall(void);
static inline void nv2a_profile_inc_counter(enum NV2A_PROF_COUNTERS_ENUM cnt)
{
g_nv2a_stats.frame_working.counters[cnt] += 1;
}
#ifdef CONFIG_RENDERDOC
void nv2a_dbg_renderdoc_init(void);
void *nv2a_dbg_renderdoc_get_api(void);
bool nv2a_dbg_renderdoc_available(void);
void nv2a_dbg_renderdoc_capture_frames(int num_frames);
extern int renderdoc_capture_frames;
#endif
#ifdef __cplusplus
}
-6
View File
@@ -1,6 +0,0 @@
softmmu_ss.add([sdl, files(
'gloffscreen_common.c',
'gloffscreen_sdl.c',
)])
# gloffscreen_sdl.o-cflags := $(SDL_CFLAGS)
+1 -11
View File
@@ -1,27 +1,17 @@
specific_ss.add(files(
'nv2a.c',
'debug.c',
'pbus.c',
'pcrtc.c',
'pfb.c',
'pfifo.c',
'pgraph.c',
'pmc.c',
'pramdac.c',
'prmcio.c',
'prmdio.c',
'prmvio.c',
'psh.c',
'ptimer.c',
'pvideo.c',
'shaders.c',
'stubs.c',
'user.c',
'vsh.c',
'swizzle.c',
's3tc.c',
))
subdir('gl')
subdir('thirdparty')
specific_ss.add(nv2a_vsh_cpu)
subdir('pgraph')
+18 -10
View File
@@ -172,6 +172,16 @@ static void nv2a_get_offsets(VGACommonState *s,
*pline_compare = line_compare;
}
const uint8_t *nv2a_get_dac_palette(void)
{
return g_nv2a->puserdac.palette;
}
int nv2a_get_screen_off(void)
{
return g_nv2a->vga.sr[VGA_SEQ_CLOCK_MODE] & VGA_SR01_SCREEN_OFF;
}
static void nv2a_vga_gfx_update(void *opaque)
{
VGACommonState *vga = opaque;
@@ -277,7 +287,7 @@ static void nv2a_reset(NV2AState *d)
}
memset(d->pfifo.regs, 0, sizeof(d->pfifo.regs));
memset(d->pgraph.regs, 0, sizeof(d->pgraph.regs));
memset(d->pgraph.regs_, 0, sizeof(d->pgraph.regs_));
memset(d->pvideo.regs, 0, sizeof(d->pvideo.regs));
d->pcrtc.start = 0;
@@ -365,11 +375,10 @@ static void nv2a_vm_state_change(void *opaque, bool running, RunState state)
if (state == RUN_STATE_SAVE_VM) {
nv2a_lock_fifo(d);
qatomic_set(&d->pfifo.halt, true);
qatomic_set(&d->pgraph.download_dirty_surfaces_pending, true);
qemu_event_reset(&d->pgraph.dirty_surfaces_download_complete);
d->pgraph.renderer->ops.pre_savevm_trigger(d);
nv2a_unlock_fifo(d);
qemu_mutex_unlock_iothread();
qemu_event_wait(&d->pgraph.dirty_surfaces_download_complete);
d->pgraph.renderer->ops.pre_savevm_wait(d);
qemu_mutex_lock_iothread();
nv2a_lock_fifo(d);
} else if (state == RUN_STATE_RESTORE_VM) {
@@ -382,11 +391,10 @@ static void nv2a_vm_state_change(void *opaque, bool running, RunState state)
nv2a_unlock_fifo(d);
} else if (state == RUN_STATE_SHUTDOWN) {
nv2a_lock_fifo(d);
qatomic_set(&d->pgraph.shader_cache_writeback_pending, true);
qemu_event_reset(&d->pgraph.shader_cache_writeback_complete);
d->pgraph.renderer->ops.pre_shutdown_trigger(d);
nv2a_unlock_fifo(d);
qemu_mutex_unlock_iothread();
qemu_event_wait(&d->pgraph.shader_cache_writeback_complete);
d->pgraph.renderer->ops.pre_shutdown_wait(d);
qemu_mutex_lock_iothread();
}
}
@@ -515,9 +523,9 @@ static const VMStateDescription vmstate_nv2a = {
VMSTATE_UINT32(pgraph.inline_buffer_length, NV2AState), // fixme
VMSTATE_UINT32(pgraph.draw_arrays_length, NV2AState),
VMSTATE_UINT32(pgraph.draw_arrays_max_count, NV2AState),
VMSTATE_INT32_ARRAY(pgraph.gl_draw_arrays_start, NV2AState, 1250),
VMSTATE_INT32_ARRAY(pgraph.gl_draw_arrays_count, NV2AState, 1250),
VMSTATE_UINT32_ARRAY(pgraph.regs, NV2AState, 0x2000),
VMSTATE_INT32_ARRAY(pgraph.draw_arrays_start, NV2AState, 1250),
VMSTATE_INT32_ARRAY(pgraph.draw_arrays_count, NV2AState, 1250),
VMSTATE_UINT32_ARRAY(pgraph.regs_, NV2AState, 0x2000),
VMSTATE_UINT32(pmc.pending_interrupts, NV2AState),
VMSTATE_UINT32(pmc.enabled_interrupts, NV2AState),
VMSTATE_UINT32(pfifo.pending_interrupts, NV2AState),
+1 -1
View File
@@ -22,7 +22,7 @@
#define HW_NV2A_H
void nv2a_init(PCIBus *bus, int devfn, MemoryRegion *ram);
void nv2a_gl_context_init(void);
void nv2a_context_init(void);
int nv2a_get_framebuffer_surface(void);
void nv2a_set_surface_scale_factor(unsigned int scale);
unsigned int nv2a_get_surface_scale_factor(void);
+1 -373
View File
@@ -44,25 +44,12 @@
#include "cpu.h"
#include "trace.h"
#include "swizzle.h"
#include "lru.h"
#include "gl/gloffscreen.h"
#include "nv2a.h"
#include "pgraph/pgraph.h"
#include "debug.h"
#include "shaders.h"
#include "nv2a_regs.h"
#define GET_MASK(v, mask) (((v) & (mask)) >> ctz32(mask))
#define SET_MASK(v, mask, val) \
({ \
const unsigned int __val = (val); \
const unsigned int __mask = (mask); \
(v) &= ~(__mask); \
(v) |= ((__val) << ctz32(__mask)) & (__mask); \
})
#define NV2A_DEVICE(obj) OBJECT_CHECK(NV2AState, (obj), "nv2a")
enum FIFOEngine {
@@ -78,347 +65,6 @@ typedef struct DMAObject {
hwaddr limit;
} DMAObject;
typedef struct VertexAttribute {
bool dma_select;
hwaddr offset;
/* inline arrays are packed in order?
* Need to pass the offset to converted attributes */
unsigned int inline_array_offset;
float inline_value[4];
unsigned int format;
unsigned int size; /* size of the data type */
unsigned int count; /* number of components */
uint32_t stride;
bool needs_conversion;
float *inline_buffer;
bool inline_buffer_populated;
GLint gl_count;
GLenum gl_type;
GLboolean gl_normalize;
GLuint gl_inline_buffer;
} VertexAttribute;
typedef struct SurfaceFormatInfo {
unsigned int bytes_per_pixel;
GLint gl_internal_format;
GLenum gl_format;
GLenum gl_type;
GLenum gl_attachment;
} SurfaceFormatInfo;
typedef struct Surface {
bool draw_dirty;
bool buffer_dirty;
bool write_enabled_cache;
unsigned int pitch;
hwaddr offset;
} Surface;
typedef struct SurfaceShape {
unsigned int z_format;
unsigned int color_format;
unsigned int zeta_format;
unsigned int log_width, log_height;
unsigned int clip_x, clip_y;
unsigned int clip_width, clip_height;
unsigned int anti_aliasing;
} SurfaceShape;
typedef struct SurfaceBinding {
QTAILQ_ENTRY(SurfaceBinding) entry;
MemAccessCallback *access_cb;
hwaddr vram_addr;
SurfaceFormatInfo fmt;
SurfaceShape shape;
uintptr_t dma_addr;
uintptr_t dma_len;
bool color;
bool swizzle;
unsigned int width;
unsigned int height;
unsigned int pitch;
size_t size;
GLuint gl_buffer;
bool cleared;
int frame_time;
int draw_time;
bool draw_dirty;
bool download_pending;
bool upload_pending;
} SurfaceBinding;
typedef struct TextureShape {
bool cubemap;
unsigned int dimensionality;
unsigned int color_format;
unsigned int levels;
unsigned int width, height, depth;
bool border;
unsigned int min_mipmap_level, max_mipmap_level;
unsigned int pitch;
} TextureShape;
typedef struct TextureBinding {
GLenum gl_target;
GLuint gl_texture;
unsigned int refcnt;
int draw_time;
uint64_t data_hash;
unsigned int scale;
unsigned int min_filter;
unsigned int mag_filter;
unsigned int addru;
unsigned int addrv;
unsigned int addrp;
uint32_t border_color;
bool border_color_set;
} TextureBinding;
typedef struct TextureKey {
TextureShape state;
hwaddr texture_vram_offset;
hwaddr texture_length;
hwaddr palette_vram_offset;
hwaddr palette_length;
} TextureKey;
typedef struct TextureLruNode {
LruNode node;
TextureKey key;
TextureBinding *binding;
bool possibly_dirty;
} TextureLruNode;
typedef struct VertexKey {
size_t count;
GLuint gl_type;
GLboolean gl_normalize;
size_t stride;
hwaddr addr;
} VertexKey;
typedef struct VertexLruNode {
LruNode node;
VertexKey key;
GLuint gl_buffer;
bool initialized;
} VertexLruNode;
typedef struct KelvinState {
hwaddr object_instance;
} KelvinState;
typedef struct ContextSurfaces2DState {
hwaddr object_instance;
hwaddr dma_image_source;
hwaddr dma_image_dest;
unsigned int color_format;
unsigned int source_pitch, dest_pitch;
hwaddr source_offset, dest_offset;
} ContextSurfaces2DState;
typedef struct ImageBlitState {
hwaddr object_instance;
hwaddr context_surfaces;
unsigned int operation;
unsigned int in_x, in_y;
unsigned int out_x, out_y;
unsigned int width, height;
} ImageBlitState;
typedef struct BetaState {
hwaddr object_instance;
uint32_t beta;
} BetaState;
typedef struct QueryReport {
QSIMPLEQ_ENTRY(QueryReport) entry;
bool clear;
uint32_t parameter;
unsigned int query_count;
GLuint *queries;
} QueryReport;
typedef struct PGRAPHState {
QemuMutex lock;
uint32_t pending_interrupts;
uint32_t enabled_interrupts;
int frame_time;
int draw_time;
struct s2t_rndr {
GLuint fbo, vao, vbo, prog;
GLuint tex_loc, surface_size_loc;
} s2t_rndr;
struct disp_rndr {
GLuint fbo, vao, vbo, prog;
GLuint display_size_loc;
GLuint line_offset_loc;
GLuint tex_loc;
GLuint pvideo_tex;
GLint pvideo_enable_loc;
GLint pvideo_tex_loc;
GLint pvideo_in_pos_loc;
GLint pvideo_pos_loc;
GLint pvideo_scale_loc;
GLint pvideo_color_key_enable_loc;
GLint pvideo_color_key_loc;
GLint palette_loc[256];
} disp_rndr;
/* subchannels state we're not sure the location of... */
ContextSurfaces2DState context_surfaces_2d;
ImageBlitState image_blit;
KelvinState kelvin;
BetaState beta;
hwaddr dma_color, dma_zeta;
Surface surface_color, surface_zeta;
unsigned int surface_type;
SurfaceShape surface_shape;
SurfaceShape last_surface_shape;
QTAILQ_HEAD(, SurfaceBinding) surfaces;
SurfaceBinding *color_binding, *zeta_binding;
struct {
int clip_x;
int clip_width;
int clip_y;
int clip_height;
int width;
int height;
} surface_binding_dim; // FIXME: Refactor
hwaddr dma_a, dma_b;
Lru texture_cache;
TextureLruNode *texture_cache_entries;
bool texture_dirty[NV2A_MAX_TEXTURES];
TextureBinding *texture_binding[NV2A_MAX_TEXTURES];
Lru shader_cache;
ShaderLruNode *shader_cache_entries;
ShaderBinding *shader_binding;
QemuMutex shader_cache_lock;
QemuThread shader_disk_thread;
bool texture_matrix_enable[NV2A_MAX_TEXTURES];
GLuint gl_framebuffer;
GLuint gl_display_buffer;
GLint gl_display_buffer_internal_format;
GLsizei gl_display_buffer_width;
GLsizei gl_display_buffer_height;
GLenum gl_display_buffer_format;
GLenum gl_display_buffer_type;
hwaddr dma_state;
hwaddr dma_notifies;
hwaddr dma_semaphore;
hwaddr dma_report;
hwaddr report_offset;
bool zpass_pixel_count_enable;
unsigned int zpass_pixel_count_result;
unsigned int gl_zpass_pixel_count_query_count;
GLuint *gl_zpass_pixel_count_queries;
QSIMPLEQ_HEAD(, QueryReport) report_queue;
hwaddr dma_vertex_a, dma_vertex_b;
uint32_t primitive_mode;
bool enable_vertex_program_write;
uint32_t vertex_state_shader_v0[4];
uint32_t program_data[NV2A_MAX_TRANSFORM_PROGRAM_LENGTH][VSH_TOKEN_SIZE];
bool program_data_dirty;
uint32_t vsh_constants[NV2A_VERTEXSHADER_CONSTANTS][4];
bool vsh_constants_dirty[NV2A_VERTEXSHADER_CONSTANTS];
/* lighting constant arrays */
uint32_t ltctxa[NV2A_LTCTXA_COUNT][4];
bool ltctxa_dirty[NV2A_LTCTXA_COUNT];
uint32_t ltctxb[NV2A_LTCTXB_COUNT][4];
bool ltctxb_dirty[NV2A_LTCTXB_COUNT];
uint32_t ltc1[NV2A_LTC1_COUNT][4];
bool ltc1_dirty[NV2A_LTC1_COUNT];
float material_alpha;
// should figure out where these are in lighting context
float light_infinite_half_vector[NV2A_MAX_LIGHTS][3];
float light_infinite_direction[NV2A_MAX_LIGHTS][3];
float light_local_position[NV2A_MAX_LIGHTS][3];
float light_local_attenuation[NV2A_MAX_LIGHTS][3];
float point_params[8];
VertexAttribute vertex_attributes[NV2A_VERTEXSHADER_ATTRIBUTES];
uint16_t compressed_attrs;
Lru element_cache;
VertexLruNode *element_cache_entries;
unsigned int inline_array_length;
uint32_t inline_array[NV2A_MAX_BATCH_LENGTH];
GLuint gl_inline_array_buffer;
unsigned int inline_elements_length;
uint32_t inline_elements[NV2A_MAX_BATCH_LENGTH];
unsigned int inline_buffer_length;
unsigned int draw_arrays_length;
unsigned int draw_arrays_min_start;
unsigned int draw_arrays_max_count;
/* FIXME: Unknown size, possibly endless, 1250 will do for now */
/* Keep in sync with size used in nv2a.c */
GLint gl_draw_arrays_start[1250];
GLsizei gl_draw_arrays_count[1250];
bool draw_arrays_prevent_connect;
GLuint gl_memory_buffer;
GLuint gl_vertex_array;
uint32_t regs[0x2000];
bool clearing;
bool waiting_for_nop;
bool waiting_for_flip;
bool waiting_for_context_switch;
bool downloads_pending;
bool download_dirty_surfaces_pending;
bool flush_pending;
bool gl_sync_pending;
bool shader_cache_writeback_pending;
QemuEvent downloads_complete;
QemuEvent dirty_surfaces_download_complete;
QemuEvent flush_complete;
QemuEvent gl_sync_complete;
QemuEvent shader_cache_writeback_complete;
unsigned int surface_scale_factor;
uint8_t *scale_buf;
} PGRAPHState;
typedef struct NV2AState {
/*< private >*/
PCIDevice parent_obj;
@@ -512,9 +158,6 @@ typedef struct NV2ABlockInfo {
} NV2ABlockInfo;
extern const NV2ABlockInfo blocktable[NV_NUM_BLOCKS];
extern GloContext *g_nv2a_context_render;
extern GloContext *g_nv2a_context_display;
void nv2a_update_irq(NV2AState *d);
static inline
@@ -566,20 +209,5 @@ DEFINE_PROTO(user)
DMAObject nv_dma_load(NV2AState *d, hwaddr dma_obj_address);
void *nv_dma_map(NV2AState *d, hwaddr dma_obj_address, hwaddr *len);
void pgraph_init(NV2AState *d);
void pgraph_destroy(PGRAPHState *pg);
void pgraph_context_switch(NV2AState *d, unsigned int channel_id);
int pgraph_method(NV2AState *d, unsigned int subchannel, unsigned int method,
uint32_t parameter, uint32_t *parameters,
size_t num_words_available, size_t max_lookahead_words,
bool inc);
void pgraph_gl_sync(NV2AState *d);
void pgraph_process_pending_reports(NV2AState *d);
void pgraph_process_pending_downloads(NV2AState *d);
void pgraph_download_dirty_surfaces(NV2AState *d);
void pgraph_flush(NV2AState *d);
void *pfifo_thread(void *arg);
void pfifo_kick(NV2AState *d);
#endif
+11
View File
@@ -21,6 +21,17 @@
#ifndef HW_NV2A_REGS_H
#define HW_NV2A_REGS_H
#define GET_MASK(v, mask) (((v) & (mask)) >> ctz32(mask))
#define SET_MASK(v, mask, val) \
({ \
const unsigned int __val = (val); \
const unsigned int __mask = (mask); \
(v) &= ~(__mask); \
(v) |= ((__val) << ctz32(__mask)) & (__mask); \
})
#define NV_NUM_BLOCKS 21
#define NV_PMC 0 /* card master control */
#define NV_PBUS 1 /* bus control */
+18 -44
View File
@@ -95,23 +95,25 @@ void pfifo_kick(NV2AState *d)
qemu_cond_broadcast(&d->pfifo.fifo_cond);
}
static bool pgraph_can_fifo_access(NV2AState *d) {
return qatomic_read(&d->pgraph.regs[NV_PGRAPH_FIFO]) & NV_PGRAPH_FIFO_ACCESS;
static bool can_fifo_access(NV2AState *d) {
return qatomic_read(&d->pgraph.regs_[NV_PGRAPH_FIFO]) &
NV_PGRAPH_FIFO_ACCESS;
}
/* If NV097_FLIP_STALL was executed, check if the flip has completed.
* This will usually happen in the VSYNC interrupt handler.
*/
static bool pgraph_is_flip_stall_complete(NV2AState *d)
static bool is_flip_stall_complete(NV2AState *d)
{
PGRAPHState *pg = &d->pgraph;
uint32_t s = pgraph_reg_r(pg, NV_PGRAPH_SURFACE);
NV2A_DPRINTF("flip stall read: %d, write: %d, modulo: %d\n",
GET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_READ_3D),
GET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_WRITE_3D),
GET_MASK(pg->regs[NV_PGRAPH_SURFACE], NV_PGRAPH_SURFACE_MODULO_3D));
GET_MASK(s, NV_PGRAPH_SURFACE_READ_3D),
GET_MASK(s, NV_PGRAPH_SURFACE_WRITE_3D),
GET_MASK(s, NV_PGRAPH_SURFACE_MODULO_3D));
uint32_t s = pg->regs[NV_PGRAPH_SURFACE];
if (GET_MASK(s, NV_PGRAPH_SURFACE_READ_3D)
!= GET_MASK(s, NV_PGRAPH_SURFACE_WRITE_3D)) {
return true;
@@ -126,7 +128,7 @@ static bool pfifo_stall_for_flip(NV2AState *d)
if (qatomic_read(&d->pgraph.waiting_for_flip)) {
qemu_mutex_lock(&d->pgraph.lock);
if (!pgraph_is_flip_stall_complete(d)) {
if (!is_flip_stall_complete(d)) {
should_stall = true;
} else {
d->pgraph.waiting_for_flip = false;
@@ -141,7 +143,7 @@ static bool pfifo_puller_should_stall(NV2AState *d)
{
return pfifo_stall_for_flip(d) || qatomic_read(&d->pgraph.waiting_for_nop) ||
qatomic_read(&d->pgraph.waiting_for_context_switch) ||
!pgraph_can_fifo_access(d);
!can_fifo_access(d);
}
static ssize_t pfifo_run_puller(NV2AState *d, uint32_t method_entry,
@@ -187,7 +189,7 @@ static ssize_t pfifo_run_puller(NV2AState *d, uint32_t method_entry,
qemu_mutex_lock(&d->pgraph.lock);
// Switch contexts if necessary
if (pgraph_can_fifo_access(d)) {
if (can_fifo_access(d)) {
pgraph_context_switch(d, entry.channel_id);
if (!d->pgraph.waiting_for_context_switch) {
num_proc =
@@ -221,7 +223,7 @@ static ssize_t pfifo_run_puller(NV2AState *d, uint32_t method_entry,
qemu_mutex_unlock(&d->pfifo.lock);
qemu_mutex_lock(&d->pgraph.lock);
if (pgraph_can_fifo_access(d)) {
if (can_fifo_access(d)) {
num_proc =
pgraph_method(d, subchannel, method, parameter, parameters,
num_words_available, max_lookahead_words, inc);
@@ -242,7 +244,7 @@ static ssize_t pfifo_run_puller(NV2AState *d, uint32_t method_entry,
static bool pfifo_pusher_should_stall(NV2AState *d)
{
return !pgraph_can_fifo_access(d) ||
return !can_fifo_access(d) ||
qatomic_read(&d->pgraph.waiting_for_nop);
}
@@ -447,39 +449,11 @@ static void pfifo_run_pusher(NV2AState *d)
}
}
static void process_requests(NV2AState *d)
{
if (qatomic_read(&d->pgraph.downloads_pending) ||
qatomic_read(&d->pgraph.download_dirty_surfaces_pending) ||
qatomic_read(&d->pgraph.gl_sync_pending) ||
qatomic_read(&d->pgraph.flush_pending) ||
qatomic_read(&d->pgraph.shader_cache_writeback_pending)) {
qemu_mutex_unlock(&d->pfifo.lock);
qemu_mutex_lock(&d->pgraph.lock);
if (qatomic_read(&d->pgraph.downloads_pending)) {
pgraph_process_pending_downloads(d);
}
if (qatomic_read(&d->pgraph.download_dirty_surfaces_pending)) {
pgraph_download_dirty_surfaces(d);
}
if (qatomic_read(&d->pgraph.gl_sync_pending)) {
pgraph_gl_sync(d);
}
if (qatomic_read(&d->pgraph.flush_pending)) {
pgraph_flush(d);
}
if (qatomic_read(&d->pgraph.shader_cache_writeback_pending)) {
shader_write_cache_reload_list(&d->pgraph);
}
qemu_mutex_unlock(&d->pgraph.lock);
qemu_mutex_lock(&d->pfifo.lock);
}
}
void *pfifo_thread(void *arg)
{
NV2AState *d = (NV2AState *)arg;
glo_set_current(g_nv2a_context_render);
pgraph_init_thread(d);
rcu_register_thread();
@@ -487,13 +461,13 @@ void *pfifo_thread(void *arg)
while (true) {
d->pfifo.fifo_kick = false;
process_requests(d);
d->pgraph.renderer->ops.process_pending(d);
if (!d->pfifo.halt) {
pfifo_run_pusher(d);
}
pgraph_process_pending_reports(d);
d->pgraph.renderer->ops.process_pending_reports(d);
if (!d->pfifo.fifo_kick) {
qemu_cond_broadcast(&d->pfifo.fifo_idle_cond);
File diff suppressed because it is too large Load Diff
+84
View File
@@ -0,0 +1,84 @@
/*
* Geforce NV2A PGRAPH Renderdoc Helpers
*
* Copyright (c) 2024 Matt Borgerson
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "qemu/osdep.h"
#include <stdint.h>
#include <stdbool.h>
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
#include "thirdparty/renderdoc_app.h"
#include "hw/xbox/nv2a/debug.h"
#ifdef _WIN32
#include <libloaderapi.h>
#else
#include <dlfcn.h>
#endif
static RENDERDOC_API_1_6_0 *rdoc_api = NULL;
int renderdoc_capture_frames = 0;
void nv2a_dbg_renderdoc_init(void)
{
if (rdoc_api) {
return;
}
#ifdef _WIN32
HMODULE renderdoc = GetModuleHandleA("renderdoc.dll");
if (renderdoc) {
pRENDERDOC_GetAPI RENDERDOC_GetAPI =
(pRENDERDOC_GetAPI)GetProcAddress(renderdoc, "RENDERDOC_GetAPI");
#else
void *renderdoc = dlopen(
#ifdef __APPLE__
"librenderdoc.dylib",
#else
"librenderdoc.so",
#endif
RTLD_LAZY);
if (renderdoc) {
pRENDERDOC_GetAPI RENDERDOC_GetAPI =
(pRENDERDOC_GetAPI)dlsym(renderdoc, "RENDERDOC_GetAPI");
#endif // _WIN32
int ret =
RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_6_0, (void **)&rdoc_api);
assert(ret == 1 && "Failed to retrieve RenderDoc API.");
} else {
fprintf(stderr, "Error: Failed to open renderdoc library: %s\n", dlerror());
}
}
void *nv2a_dbg_renderdoc_get_api(void)
{
return (void*)rdoc_api;
}
bool nv2a_dbg_renderdoc_available(void)
{
return rdoc_api != NULL;
}
void nv2a_dbg_renderdoc_capture_frames(int num_frames)
{
renderdoc_capture_frames += num_frames;
}
+174
View File
@@ -0,0 +1,174 @@
/*
* Geforce NV2A PGRAPH OpenGL Renderer
*
* Copyright (c) 2012 espes
* Copyright (c) 2015 Jannik Vogel
* Copyright (c) 2018-2024 Matt Borgerson
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "hw/xbox/nv2a/nv2a_int.h"
#include "renderer.h"
// TODO: Optimize. Ideally this should all be done via OpenGL.
void pgraph_gl_image_blit(NV2AState *d)
{
PGRAPHState *pg = &d->pgraph;
ContextSurfaces2DState *context_surfaces = &pg->context_surfaces_2d;
ImageBlitState *image_blit = &pg->image_blit;
BetaState *beta = &pg->beta;
pgraph_gl_surface_update(d, false, true, true);
assert(context_surfaces->object_instance == image_blit->context_surfaces);
unsigned int bytes_per_pixel;
switch (context_surfaces->color_format) {
case NV062_SET_COLOR_FORMAT_LE_Y8:
bytes_per_pixel = 1;
break;
case NV062_SET_COLOR_FORMAT_LE_R5G6B5:
bytes_per_pixel = 2;
break;
case NV062_SET_COLOR_FORMAT_LE_A8R8G8B8:
case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8:
case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8_Z8R8G8B8:
case NV062_SET_COLOR_FORMAT_LE_Y32:
bytes_per_pixel = 4;
break;
default:
fprintf(stderr, "Unknown blit surface format: 0x%x\n",
context_surfaces->color_format);
assert(false);
break;
}
hwaddr source_dma_len, dest_dma_len;
uint8_t *source = (uint8_t *)nv_dma_map(
d, context_surfaces->dma_image_source, &source_dma_len);
assert(context_surfaces->source_offset < source_dma_len);
source += context_surfaces->source_offset;
uint8_t *dest = (uint8_t *)nv_dma_map(d, context_surfaces->dma_image_dest,
&dest_dma_len);
assert(context_surfaces->dest_offset < dest_dma_len);
dest += context_surfaces->dest_offset;
hwaddr source_addr = source - d->vram_ptr;
hwaddr dest_addr = dest - d->vram_ptr;
SurfaceBinding *surf_src = pgraph_gl_surface_get(d, source_addr);
if (surf_src) {
pgraph_gl_surface_download_if_dirty(d, surf_src);
}
SurfaceBinding *surf_dest = pgraph_gl_surface_get(d, dest_addr);
if (surf_dest) {
if (image_blit->height < surf_dest->height ||
image_blit->width < surf_dest->width) {
pgraph_gl_surface_download_if_dirty(d, surf_dest);
} else {
// The blit will completely replace the surface so any pending
// download should be discarded.
surf_dest->download_pending = false;
surf_dest->draw_dirty = false;
}
surf_dest->upload_pending = true;
pg->draw_time++;
}
hwaddr source_offset = image_blit->in_y * context_surfaces->source_pitch +
image_blit->in_x * bytes_per_pixel;
hwaddr dest_offset = image_blit->out_y * context_surfaces->dest_pitch +
image_blit->out_x * bytes_per_pixel;
hwaddr source_size =
(image_blit->height - 1) * context_surfaces->source_pitch +
image_blit->width * bytes_per_pixel;
hwaddr dest_size = (image_blit->height - 1) * context_surfaces->dest_pitch +
image_blit->width * bytes_per_pixel;
/* FIXME: What does hardware do in this case? */
assert(source_addr + source_offset + source_size <=
memory_region_size(d->vram));
assert(dest_addr + dest_offset + dest_size <= memory_region_size(d->vram));
uint8_t *source_row = source + source_offset;
uint8_t *dest_row = dest + dest_offset;
if (image_blit->operation == NV09F_SET_OPERATION_SRCCOPY) {
// NV2A_GL_DPRINTF(false, "NV09F_SET_OPERATION_SRCCOPY");
for (unsigned int y = 0; y < image_blit->height; y++) {
memmove(dest_row, source_row, image_blit->width * bytes_per_pixel);
source_row += context_surfaces->source_pitch;
dest_row += context_surfaces->dest_pitch;
}
} else if (image_blit->operation == NV09F_SET_OPERATION_BLEND_AND) {
// NV2A_GL_DPRINTF(false, "NV09F_SET_OPERATION_BLEND_AND");
uint32_t max_beta_mult = 0x7f80;
uint32_t beta_mult = beta->beta >> 16;
uint32_t inv_beta_mult = max_beta_mult - beta_mult;
for (unsigned int y = 0; y < image_blit->height; y++) {
for (unsigned int x = 0; x < image_blit->width; x++) {
for (unsigned int ch = 0; ch < 3; ch++) {
uint32_t a = source_row[x * 4 + ch] * beta_mult;
uint32_t b = dest_row[x * 4 + ch] * inv_beta_mult;
dest_row[x * 4 + ch] = (a + b) / max_beta_mult;
}
}
source_row += context_surfaces->source_pitch;
dest_row += context_surfaces->dest_pitch;
}
} else {
fprintf(stderr, "Unknown blit operation: 0x%x\n",
image_blit->operation);
assert(false && "Unknown blit operation");
}
NV2A_DPRINTF(" - 0x%tx -> 0x%tx\n", source_addr, dest_addr);
bool needs_alpha_patching;
uint8_t alpha_override;
switch (context_surfaces->color_format) {
case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8:
needs_alpha_patching = true;
alpha_override = 0xff;
break;
case NV062_SET_COLOR_FORMAT_LE_X8R8G8B8_Z8R8G8B8:
needs_alpha_patching = true;
alpha_override = 0;
break;
default:
needs_alpha_patching = false;
alpha_override = 0;
}
if (needs_alpha_patching) {
dest_row = dest + dest_offset;
for (unsigned int y = 0; y < image_blit->height; y++) {
for (unsigned int x = 0; x < image_blit->width; x++) {
dest_row[x * 4 + 3] = alpha_override;
}
dest_row += context_surfaces->dest_pitch;
}
}
dest_addr += dest_offset;
memory_region_set_client_dirty(d->vram, dest_addr, dest_size,
DIRTY_MEMORY_VGA);
memory_region_set_client_dirty(d->vram, dest_addr, dest_size,
DIRTY_MEMORY_NV2A_TEX);
}
+322
View File
@@ -0,0 +1,322 @@
/*
* Geforce NV2A PGRAPH OpenGL Renderer
*
* Copyright (c) 2012 espes
* Copyright (c) 2015 Jannik Vogel
* Copyright (c) 2018-2024 Matt Borgerson
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef HW_XBOX_NV2A_PGRAPH_GL_CONSTANTS_H
#define HW_XBOX_NV2A_PGRAPH_GL_CONSTANTS_H
#include "qemu/osdep.h"
#include "hw/xbox/nv2a/nv2a_regs.h"
#include "gloffscreen.h"
static const GLenum pgraph_texture_min_filter_gl_map[] = {
0,
GL_NEAREST,
GL_LINEAR,
GL_NEAREST_MIPMAP_NEAREST,
GL_LINEAR_MIPMAP_NEAREST,
GL_NEAREST_MIPMAP_LINEAR,
GL_LINEAR_MIPMAP_LINEAR,
GL_LINEAR,
};
static const GLenum pgraph_texture_mag_filter_gl_map[] = {
0,
GL_NEAREST,
GL_LINEAR,
0,
GL_LINEAR /* TODO: Convolution filter... */
};
static const GLenum pgraph_texture_addr_gl_map[] = {
0,
GL_REPEAT,
GL_MIRRORED_REPEAT,
GL_CLAMP_TO_EDGE,
GL_CLAMP_TO_BORDER,
GL_CLAMP_TO_EDGE, /* Approximate GL_CLAMP */
};
static const GLenum pgraph_blend_factor_gl_map[] = {
GL_ZERO,
GL_ONE,
GL_SRC_COLOR,
GL_ONE_MINUS_SRC_COLOR,
GL_SRC_ALPHA,
GL_ONE_MINUS_SRC_ALPHA,
GL_DST_ALPHA,
GL_ONE_MINUS_DST_ALPHA,
GL_DST_COLOR,
GL_ONE_MINUS_DST_COLOR,
GL_SRC_ALPHA_SATURATE,
0,
GL_CONSTANT_COLOR,
GL_ONE_MINUS_CONSTANT_COLOR,
GL_CONSTANT_ALPHA,
GL_ONE_MINUS_CONSTANT_ALPHA,
};
static const GLenum pgraph_blend_equation_gl_map[] = {
GL_FUNC_SUBTRACT,
GL_FUNC_REVERSE_SUBTRACT,
GL_FUNC_ADD,
GL_MIN,
GL_MAX,
GL_FUNC_REVERSE_SUBTRACT,
GL_FUNC_ADD,
};
/* FIXME
static const GLenum pgraph_blend_logicop_map[] = {
GL_CLEAR,
GL_AND,
GL_AND_REVERSE,
GL_COPY,
GL_AND_INVERTED,
GL_NOOP,
GL_XOR,
GL_OR,
GL_NOR,
GL_EQUIV,
GL_INVERT,
GL_OR_REVERSE,
GL_COPY_INVERTED,
GL_OR_INVERTED,
GL_NAND,
GL_SET,
};
*/
static const GLenum pgraph_cull_face_gl_map[] = {
0,
GL_FRONT,
GL_BACK,
GL_FRONT_AND_BACK
};
static const GLenum pgraph_depth_func_gl_map[] = {
GL_NEVER,
GL_LESS,
GL_EQUAL,
GL_LEQUAL,
GL_GREATER,
GL_NOTEQUAL,
GL_GEQUAL,
GL_ALWAYS,
};
static const GLenum pgraph_stencil_func_gl_map[] = {
GL_NEVER,
GL_LESS,
GL_EQUAL,
GL_LEQUAL,
GL_GREATER,
GL_NOTEQUAL,
GL_GEQUAL,
GL_ALWAYS,
};
static const GLenum pgraph_stencil_op_gl_map[] = {
0,
GL_KEEP,
GL_ZERO,
GL_REPLACE,
GL_INCR,
GL_DECR,
GL_INVERT,
GL_INCR_WRAP,
GL_DECR_WRAP,
};
typedef struct ColorFormatInfo {
unsigned int bytes_per_pixel;
bool linear;
GLint gl_internal_format;
GLenum gl_format;
GLenum gl_type;
GLenum gl_swizzle_mask[4];
bool depth;
} ColorFormatInfo;
static const ColorFormatInfo kelvin_color_format_gl_map[66] = {
[NV097_SET_TEXTURE_FORMAT_COLOR_SZ_Y8] =
{1, false, GL_R8, GL_RED, GL_UNSIGNED_BYTE,
{GL_RED, GL_RED, GL_RED, GL_ONE}},
[NV097_SET_TEXTURE_FORMAT_COLOR_SZ_AY8] =
{1, false, GL_R8, GL_RED, GL_UNSIGNED_BYTE,
{GL_RED, GL_RED, GL_RED, GL_RED}},
[NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A1R5G5B5] =
{2, false, GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV},
[NV097_SET_TEXTURE_FORMAT_COLOR_SZ_X1R5G5B5] =
{2, false, GL_RGB5, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV},
[NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A4R4G4B4] =
{2, false, GL_RGBA4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV},
[NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R5G6B5] =
{2, false, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5},
[NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8R8G8B8] =
{4, false, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV},
[NV097_SET_TEXTURE_FORMAT_COLOR_SZ_X8R8G8B8] =
{4, false, GL_RGB8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV},
/* paletted texture */
[NV097_SET_TEXTURE_FORMAT_COLOR_SZ_I8_A8R8G8B8] =
{1, false, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV},
[NV097_SET_TEXTURE_FORMAT_COLOR_L_DXT1_A1R5G5B5] =
{4, false, GL_COMPRESSED_RGBA_S3TC_DXT1_EXT, 0, GL_RGBA},
[NV097_SET_TEXTURE_FORMAT_COLOR_L_DXT23_A8R8G8B8] =
{4, false, GL_COMPRESSED_RGBA_S3TC_DXT3_EXT, 0, GL_RGBA},
[NV097_SET_TEXTURE_FORMAT_COLOR_L_DXT45_A8R8G8B8] =
{4, false, GL_COMPRESSED_RGBA_S3TC_DXT5_EXT, 0, GL_RGBA},
[NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A1R5G5B5] =
{2, true, GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV},
[NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R5G6B5] =
{2, true, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5},
[NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8R8G8B8] =
{4, true, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV},
[NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_Y8] =
{1, true, GL_R8, GL_RED, GL_UNSIGNED_BYTE,
{GL_RED, GL_RED, GL_RED, GL_ONE}},
[NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_G8B8] =
{2, true, GL_RG8, GL_RG, GL_UNSIGNED_BYTE,
{GL_RED, GL_GREEN, GL_RED, GL_GREEN}},
[NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8] =
{1, false, GL_R8, GL_RED, GL_UNSIGNED_BYTE,
{GL_ONE, GL_ONE, GL_ONE, GL_RED}},
[NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8Y8] =
{2, false, GL_RG8, GL_RG, GL_UNSIGNED_BYTE,
{GL_RED, GL_RED, GL_RED, GL_GREEN}},
[NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_AY8] =
{1, true, GL_R8, GL_RED, GL_UNSIGNED_BYTE,
{GL_RED, GL_RED, GL_RED, GL_RED}},
[NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_X1R5G5B5] =
{2, true, GL_RGB5, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV},
[NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A4R4G4B4] =
{2, true, GL_RGBA4, GL_BGRA, GL_UNSIGNED_SHORT_4_4_4_4_REV},
[NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_X8R8G8B8] =
{4, true, GL_RGB8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV},
[NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8] =
{1, true, GL_R8, GL_RED, GL_UNSIGNED_BYTE,
{GL_ONE, GL_ONE, GL_ONE, GL_RED}},
[NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8Y8] =
{2, true, GL_RG8, GL_RG, GL_UNSIGNED_BYTE,
{GL_RED, GL_RED, GL_RED, GL_GREEN}},
[NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R6G5B5] =
{2, false, GL_RGB8_SNORM, GL_RGB, GL_BYTE}, /* FIXME: This might be signed */
[NV097_SET_TEXTURE_FORMAT_COLOR_SZ_G8B8] =
{2, false, GL_RG8, GL_RG, GL_UNSIGNED_BYTE,
{GL_RED, GL_GREEN, GL_RED, GL_GREEN}},
[NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R8B8] =
{2, false, GL_RG8, GL_RG, GL_UNSIGNED_BYTE,
{GL_GREEN, GL_RED, GL_RED, GL_GREEN}},
[NV097_SET_TEXTURE_FORMAT_COLOR_LC_IMAGE_CR8YB8CB8YA8] =
{2, true, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV},
[NV097_SET_TEXTURE_FORMAT_COLOR_LC_IMAGE_YB8CR8YA8CB8] =
{2, true, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV},
/* Additional information is passed to the pixel shader via the swizzle:
* RED: The depth value.
* GREEN: 0 for 16-bit, 1 for 24 bit
* BLUE: 0 for fixed, 1 for float
*/
[NV097_SET_TEXTURE_FORMAT_COLOR_SZ_DEPTH_Y16_FIXED] =
{2, false, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT,
{GL_RED, GL_ZERO, GL_ZERO, GL_ZERO}, true},
[NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_DEPTH_X8_Y24_FIXED] =
{4, true, GL_DEPTH_COMPONENT, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8,
{GL_RED, GL_ONE, GL_ZERO, GL_ZERO}, true},
[NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_DEPTH_X8_Y24_FLOAT] =
/* FIXME: Uses fixed-point format to match surface format hack below. */
{4, true, GL_DEPTH_COMPONENT, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8,
{GL_RED, GL_ONE, GL_ZERO, GL_ZERO}, true},
[NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_DEPTH_Y16_FIXED] =
{2, true, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT,
{GL_RED, GL_ZERO, GL_ZERO, GL_ZERO}, true},
[NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_DEPTH_Y16_FLOAT] =
{2, true, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_HALF_FLOAT,
{GL_RED, GL_ZERO, GL_ONE, GL_ZERO}, true},
[NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_Y16] =
{2, true, GL_R16, GL_RED, GL_UNSIGNED_SHORT,
{GL_RED, GL_RED, GL_RED, GL_ONE}},
[NV097_SET_TEXTURE_FORMAT_COLOR_SZ_A8B8G8R8] =
{4, false, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV},
[NV097_SET_TEXTURE_FORMAT_COLOR_SZ_B8G8R8A8] =
{4, false, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8},
[NV097_SET_TEXTURE_FORMAT_COLOR_SZ_R8G8B8A8] =
{4, false, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8},
[NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_A8B8G8R8] =
{4, true, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8_REV},
[NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_B8G8R8A8] =
{4, true, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8},
[NV097_SET_TEXTURE_FORMAT_COLOR_LU_IMAGE_R8G8B8A8] =
{4, true, GL_RGBA8, GL_RGBA, GL_UNSIGNED_INT_8_8_8_8}
};
typedef struct SurfaceFormatInfo {
unsigned int bytes_per_pixel;
GLint gl_internal_format;
GLenum gl_format;
GLenum gl_type;
GLenum gl_attachment;
} SurfaceFormatInfo;
static const SurfaceFormatInfo kelvin_surface_color_format_gl_map[] = {
[NV097_SET_SURFACE_FORMAT_COLOR_LE_X1R5G5B5_Z1R5G5B5] =
{2, GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV, GL_COLOR_ATTACHMENT0},
[NV097_SET_SURFACE_FORMAT_COLOR_LE_R5G6B5] =
{2, GL_RGB565, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, GL_COLOR_ATTACHMENT0},
[NV097_SET_SURFACE_FORMAT_COLOR_LE_X8R8G8B8_Z8R8G8B8] =
{4, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, GL_COLOR_ATTACHMENT0},
[NV097_SET_SURFACE_FORMAT_COLOR_LE_A8R8G8B8] =
{4, GL_RGBA8, GL_BGRA, GL_UNSIGNED_INT_8_8_8_8_REV, GL_COLOR_ATTACHMENT0},
// FIXME: Map channel color
[NV097_SET_SURFACE_FORMAT_COLOR_LE_B8] =
{1, GL_R8, GL_RED, GL_UNSIGNED_BYTE, GL_COLOR_ATTACHMENT0},
[NV097_SET_SURFACE_FORMAT_COLOR_LE_G8B8] =
{2, GL_RG8, GL_RG, GL_UNSIGNED_SHORT, GL_COLOR_ATTACHMENT0},
};
static const SurfaceFormatInfo kelvin_surface_zeta_float_format_gl_map[] = {
[NV097_SET_SURFACE_FORMAT_ZETA_Z16] =
{2, GL_DEPTH_COMPONENT32F, GL_DEPTH_COMPONENT, GL_HALF_FLOAT, GL_DEPTH_ATTACHMENT},
[NV097_SET_SURFACE_FORMAT_ZETA_Z24S8] =
/* FIXME: GL does not support packing floating-point Z24S8 OOTB, so for
* now just emulate this with fixed-point Z24S8. Possible compat
* improvement with custom conversion.
*/
{4, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, GL_DEPTH_STENCIL_ATTACHMENT},
};
static const SurfaceFormatInfo kelvin_surface_zeta_fixed_format_gl_map[] = {
[NV097_SET_SURFACE_FORMAT_ZETA_Z16] =
{2, GL_DEPTH_COMPONENT16, GL_DEPTH_COMPONENT, GL_UNSIGNED_SHORT, GL_DEPTH_ATTACHMENT},
[NV097_SET_SURFACE_FORMAT_ZETA_Z24S8] =
{4, GL_DEPTH24_STENCIL8, GL_DEPTH_STENCIL, GL_UNSIGNED_INT_24_8, GL_DEPTH_STENCIL_ATTACHMENT},
};
#endif
@@ -1,5 +1,5 @@
/*
* QEMU Geforce NV2A debug helpers
* Geforce NV2A PGRAPH OpenGL Renderer
*
* Copyright (c) 2015 Jannik Vogel
* Copyright (c) 2012 espes
@@ -18,6 +18,7 @@
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "renderer.h"
#include "debug.h"
#ifdef DEBUG_NV2A_GL
@@ -28,15 +29,8 @@
#include <assert.h>
#ifdef CONFIG_RENDERDOC
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
#include "thirdparty/renderdoc_app.h"
#ifdef _WIN32
#include <libloaderapi.h>
#else
#include <dlfcn.h>
#endif
static RENDERDOC_API_1_1_2 *rdoc_api = NULL;
static int32_t renderdoc_capture_frames = 0;
#endif
#define CHECK_GL_ERROR() do { \
@@ -74,31 +68,7 @@ void gl_debug_initialize(void)
}
#ifdef CONFIG_RENDERDOC
const char *renderdoc_lib;
void* renderdoc;
#ifdef __APPLE__
renderdoc_lib = "librenderdoc.dylib";
#elif _WIN32
renderdoc_lib = "renderdoc.dll";
#else
renderdoc_lib = "librenderdoc.so";
#endif
#ifdef _WIN32
renderdoc = GetModuleHandleA(renderdoc_lib);
if (renderdoc) {
pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)GetProcAddress(
renderdoc, "RENDERDOC_GetAPI");
#else
renderdoc = dlopen(renderdoc_lib, RTLD_NOW | RTLD_NOLOAD);
if (renderdoc) {
pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)dlsym(
renderdoc, "RENDERDOC_GetAPI");
#endif
int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2,
(void **)&rdoc_api);
assert(ret == 1 && "Failed to retrieve RenderDoc API.");
}
nv2a_dbg_renderdoc_init();
#endif
}
@@ -179,7 +149,10 @@ void gl_debug_frame_terminator(void)
CHECK_GL_ERROR();
#ifdef CONFIG_RENDERDOC
if (rdoc_api) {
if (nv2a_dbg_renderdoc_available()) {
RENDERDOC_API_1_6_0 *rdoc_api = nv2a_dbg_renderdoc_get_api();
if (rdoc_api->IsTargetControlConnected()) {
if (rdoc_api->IsFrameCapturing()) {
rdoc_api->EndFrameCapture(NULL, NULL);
@@ -190,7 +163,7 @@ void gl_debug_frame_terminator(void)
error);
}
}
if (renderdoc_capture_frames) {
if (renderdoc_capture_frames > 0) {
rdoc_api->StartFrameCapture(NULL, NULL);
GLenum error = glGetError();
if (error != GL_NO_ERROR) {
@@ -203,22 +176,10 @@ void gl_debug_frame_terminator(void)
}
}
#endif
if (!has_GL_GREMEDY_frame_terminator) {
return;
if (has_GL_GREMEDY_frame_terminator) {
glFrameTerminatorGREMEDY();
CHECK_GL_ERROR();
}
glFrameTerminatorGREMEDY();
CHECK_GL_ERROR();
}
#ifdef CONFIG_RENDERDOC
bool nv2a_dbg_renderdoc_available(void) {
return rdoc_api != NULL;
}
void nv2a_dbg_renderdoc_capture_frames(uint32_t num_frames) {
renderdoc_capture_frames = num_frames;
}
#endif
#endif // DEBUG_NV2A_GL
+60
View File
@@ -0,0 +1,60 @@
/*
* Geforce NV2A PGRAPH OpenGL Renderer
*
* Copyright (c) 2015 Jannik Vogel
* Copyright (c) 2012 espes
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#ifndef HW_XBOX_NV2A_PGRAPH_GL_DEBUG_H
#define HW_XBOX_NV2A_PGRAPH_GL_DEBUG_H
// #define DEBUG_NV2A_GL
#ifdef DEBUG_NV2A_GL
#include <stdbool.h>
#include "gloffscreen.h"
#include "config-host.h"
void gl_debug_initialize(void);
void gl_debug_message(bool cc, const char *fmt, ...);
void gl_debug_group_begin(const char *fmt, ...);
void gl_debug_group_end(void);
void gl_debug_label(GLenum target, GLuint name, const char *fmt, ...);
void gl_debug_frame_terminator(void);
# define NV2A_GL_DPRINTF(cc, format, ...) \
gl_debug_message(cc, "nv2a: " format, ## __VA_ARGS__)
# define NV2A_GL_DGROUP_BEGIN(format, ...) \
gl_debug_group_begin("nv2a: " format, ## __VA_ARGS__)
# define NV2A_GL_DGROUP_END() \
gl_debug_group_end()
# define NV2A_GL_DLABEL(target, name, format, ...) \
gl_debug_label(target, name, "nv2a: { " format " }", ## __VA_ARGS__)
#define NV2A_GL_DFRAME_TERMINATOR() \
gl_debug_frame_terminator()
#else
# define NV2A_GL_DPRINTF(cc, format, ...) do { \
if (cc) NV2A_DPRINTF(format "\n", ##__VA_ARGS__ ); \
} while (0)
# define NV2A_GL_DGROUP_BEGIN(format, ...) do { } while (0)
# define NV2A_GL_DGROUP_END() do { } while (0)
# define NV2A_GL_DLABEL(target, name, format, ...) do { } while (0)
# define NV2A_GL_DFRAME_TERMINATOR() do { } while (0)
#endif
#endif
+407
View File
@@ -0,0 +1,407 @@
/*
* Geforce NV2A PGRAPH OpenGL Renderer
*
* Copyright (c) 2012 espes
* Copyright (c) 2015 Jannik Vogel
* Copyright (c) 2018-2024 Matt Borgerson
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, see <http://www.gnu.org/licenses/>.
*/
#include "hw/xbox/nv2a/nv2a_int.h"
#include "hw/xbox/nv2a/pgraph/util.h"
#include "renderer.h"
#include <math.h>
void pgraph_gl_init_display_renderer(NV2AState *d)
{
struct PGRAPHState *pg = &d->pgraph;
PGRAPHGLState *r = pg->gl_renderer_state;
glGenTextures(1, &r->gl_display_buffer);
r->gl_display_buffer_internal_format = 0;
r->gl_display_buffer_width = 0;
r->gl_display_buffer_height = 0;
r->gl_display_buffer_format = 0;
r->gl_display_buffer_type = 0;
const char *vs =
"#version 330\n"
"void main()\n"
"{\n"
" float x = -1.0 + float((gl_VertexID & 1) << 2);\n"
" float y = -1.0 + float((gl_VertexID & 2) << 1);\n"
" gl_Position = vec4(x, y, 0, 1);\n"
"}\n";
/* FIXME: improve interlace handling, pvideo */
const char *fs =
"#version 330\n"
"uniform sampler2D tex;\n"
"uniform bool pvideo_enable;\n"
"uniform sampler2D pvideo_tex;\n"
"uniform vec2 pvideo_in_pos;\n"
"uniform vec4 pvideo_pos;\n"
"uniform vec3 pvideo_scale;\n"
"uniform bool pvideo_color_key_enable;\n"
"uniform vec4 pvideo_color_key;\n"
"uniform vec2 display_size;\n"
"uniform float line_offset;\n"
"layout(location = 0) out vec4 out_Color;\n"
"void main()\n"
"{\n"
" vec2 texCoord = gl_FragCoord.xy/display_size;\n"
" float rel = display_size.y/textureSize(tex, 0).y/line_offset;\n"
" texCoord.y = 1 + rel*(texCoord.y - 1);"
" out_Color.rgba = texture(tex, texCoord);\n"
" if (pvideo_enable) {\n"
" vec2 screenCoord = gl_FragCoord.xy - 0.5;\n"
" vec4 output_region = vec4(pvideo_pos.xy, pvideo_pos.xy + pvideo_pos.zw);\n"
" bvec4 clip = bvec4(lessThan(screenCoord, output_region.xy),\n"
" greaterThan(screenCoord, output_region.zw));\n"
" if (!any(clip) && (!pvideo_color_key_enable || out_Color.rgba == pvideo_color_key)) {\n"
" vec2 out_xy = (screenCoord - pvideo_pos.xy) * pvideo_scale.z;\n"
" vec2 in_st = (pvideo_in_pos + out_xy * pvideo_scale.xy) / textureSize(pvideo_tex, 0);\n"
" in_st.y *= -1.0;\n"
" out_Color.rgba = texture(pvideo_tex, in_st);\n"
" }\n"
" }\n"
"}\n";
r->disp_rndr.prog = pgraph_gl_compile_shader(vs, fs);
r->disp_rndr.tex_loc = glGetUniformLocation(r->disp_rndr.prog, "tex");
r->disp_rndr.pvideo_enable_loc = glGetUniformLocation(r->disp_rndr.prog, "pvideo_enable");
r->disp_rndr.pvideo_tex_loc = glGetUniformLocation(r->disp_rndr.prog, "pvideo_tex");
r->disp_rndr.pvideo_in_pos_loc = glGetUniformLocation(r->disp_rndr.prog, "pvideo_in_pos");
r->disp_rndr.pvideo_pos_loc = glGetUniformLocation(r->disp_rndr.prog, "pvideo_pos");
r->disp_rndr.pvideo_scale_loc = glGetUniformLocation(r->disp_rndr.prog, "pvideo_scale");
r->disp_rndr.pvideo_color_key_enable_loc = glGetUniformLocation(r->disp_rndr.prog, "pvideo_color_key_enable");
r->disp_rndr.pvideo_color_key_loc = glGetUniformLocation(r->disp_rndr.prog, "pvideo_color_key");
r->disp_rndr.display_size_loc = glGetUniformLocation(r->disp_rndr.prog, "display_size");
r->disp_rndr.line_offset_loc = glGetUniformLocation(r->disp_rndr.prog, "line_offset");
glGenVertexArrays(1, &r->disp_rndr.vao);
glBindVertexArray(r->disp_rndr.vao);
glGenBuffers(1, &r->disp_rndr.vbo);
glBindBuffer(GL_ARRAY_BUFFER, r->disp_rndr.vbo);
glBufferData(GL_ARRAY_BUFFER, 0, NULL, GL_STATIC_DRAW);
glGenFramebuffers(1, &r->disp_rndr.fbo);
glGenTextures(1, &r->disp_rndr.pvideo_tex);
assert(glGetError() == GL_NO_ERROR);
}
static uint8_t *convert_texture_data__CR8YB8CB8YA8(const uint8_t *data,
unsigned int width,
unsigned int height,
unsigned int pitch)
{
uint8_t *converted_data = (uint8_t *)g_malloc(width * height * 4);
int x, y;
for (y = 0; y < height; y++) {
const uint8_t *line = &data[y * pitch];
const uint32_t row_offset = y * width;
for (x = 0; x < width; x++) {
uint8_t *pixel = &converted_data[(row_offset + x) * 4];
convert_yuy2_to_rgb(line, x, &pixel[0], &pixel[1], &pixel[2]);
pixel[3] = 255;
}
}
return converted_data;
}
static float pvideo_calculate_scale(unsigned int din_dout,
unsigned int output_size)
{
float calculated_in = din_dout * (output_size - 1);
calculated_in = floorf(calculated_in / (1 << 20) + 0.5f);
return (calculated_in + 1.0f) / output_size;
}
static void render_display_pvideo_overlay(NV2AState *d)
{
PGRAPHState *pg = &d->pgraph;
PGRAPHGLState *r = pg->gl_renderer_state;
// FIXME: This check against PVIDEO_SIZE_IN does not match HW behavior.
// Many games seem to pass this value when initializing or tearing down
// PVIDEO. On its own, this generally does not result in the overlay being
// hidden, however there are certain games (e.g., Ultimate Beach Soccer)
// that use an unknown mechanism to hide the overlay without explicitly
// stopping it.
// Since the value seems to be set to 0xFFFFFFFF only in cases where the
// content is not valid, it is probably good enough to treat it as an
// implicit stop.
bool enabled = (d->pvideo.regs[NV_PVIDEO_BUFFER] & NV_PVIDEO_BUFFER_0_USE)
&& d->pvideo.regs[NV_PVIDEO_SIZE_IN] != 0xFFFFFFFF;
glUniform1ui(r->disp_rndr.pvideo_enable_loc, enabled);
if (!enabled) {
return;
}
hwaddr base = d->pvideo.regs[NV_PVIDEO_BASE];
hwaddr limit = d->pvideo.regs[NV_PVIDEO_LIMIT];
hwaddr offset = d->pvideo.regs[NV_PVIDEO_OFFSET];
int in_width =
GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_IN], NV_PVIDEO_SIZE_IN_WIDTH);
int in_height =
GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_IN], NV_PVIDEO_SIZE_IN_HEIGHT);
int in_s = GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_IN],
NV_PVIDEO_POINT_IN_S);
int in_t = GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_IN],
NV_PVIDEO_POINT_IN_T);
int in_pitch =
GET_MASK(d->pvideo.regs[NV_PVIDEO_FORMAT], NV_PVIDEO_FORMAT_PITCH);
int in_color =
GET_MASK(d->pvideo.regs[NV_PVIDEO_FORMAT], NV_PVIDEO_FORMAT_COLOR);
unsigned int out_width =
GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_OUT], NV_PVIDEO_SIZE_OUT_WIDTH);
unsigned int out_height =
GET_MASK(d->pvideo.regs[NV_PVIDEO_SIZE_OUT], NV_PVIDEO_SIZE_OUT_HEIGHT);
float scale_x = 1.0f;
float scale_y = 1.0f;
unsigned int ds_dx = d->pvideo.regs[NV_PVIDEO_DS_DX];
unsigned int dt_dy = d->pvideo.regs[NV_PVIDEO_DT_DY];
if (ds_dx != NV_PVIDEO_DIN_DOUT_UNITY) {
scale_x = pvideo_calculate_scale(ds_dx, out_width);
}
if (dt_dy != NV_PVIDEO_DIN_DOUT_UNITY) {
scale_y = pvideo_calculate_scale(dt_dy, out_height);
}
// On HW, setting NV_PVIDEO_SIZE_IN larger than NV_PVIDEO_SIZE_OUT results
// in them being capped to the output size, content is not scaled. This is
// particularly important as NV_PVIDEO_SIZE_IN may be set to 0xFFFFFFFF
// during initialization or teardown.
if (in_width > out_width) {
in_width = floorf((float)out_width * scale_x + 0.5f);
}
if (in_height > out_height) {
in_height = floorf((float)out_height * scale_y + 0.5f);
}
/* TODO: support other color formats */
assert(in_color == NV_PVIDEO_FORMAT_COLOR_LE_CR8YB8CB8YA8);
unsigned int out_x =
GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_OUT], NV_PVIDEO_POINT_OUT_X);
unsigned int out_y =
GET_MASK(d->pvideo.regs[NV_PVIDEO_POINT_OUT], NV_PVIDEO_POINT_OUT_Y);
unsigned int color_key_enabled =
GET_MASK(d->pvideo.regs[NV_PVIDEO_FORMAT], NV_PVIDEO_FORMAT_DISPLAY);
glUniform1ui(r->disp_rndr.pvideo_color_key_enable_loc,
color_key_enabled);
// TODO: Verify that masking off the top byte is correct.
// SeaBlade sets a color key of 0x80000000 but the texture passed into the
// shader is cleared to 0 alpha.
unsigned int color_key = d->pvideo.regs[NV_PVIDEO_COLOR_KEY] & 0xFFFFFF;
glUniform4f(r->disp_rndr.pvideo_color_key_loc,
GET_MASK(color_key, NV_PVIDEO_COLOR_KEY_RED) / 255.0,
GET_MASK(color_key, NV_PVIDEO_COLOR_KEY_GREEN) / 255.0,
GET_MASK(color_key, NV_PVIDEO_COLOR_KEY_BLUE) / 255.0,
GET_MASK(color_key, NV_PVIDEO_COLOR_KEY_ALPHA) / 255.0);
assert(offset + in_pitch * in_height <= limit);
hwaddr end = base + offset + in_pitch * in_height;
assert(end <= memory_region_size(d->vram));
pgraph_apply_scaling_factor(pg, &out_x, &out_y);
pgraph_apply_scaling_factor(pg, &out_width, &out_height);
// Translate for the GL viewport origin.
out_y = MAX(r->gl_display_buffer_height - 1 - (int)(out_y + out_height), 0);
glActiveTexture(GL_TEXTURE0 + 1);
glBindTexture(GL_TEXTURE_2D, r->disp_rndr.pvideo_tex);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
uint8_t *tex_rgba = convert_texture_data__CR8YB8CB8YA8(
d->vram_ptr + base + offset, in_width, in_height, in_pitch);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, in_width, in_height, 0, GL_RGBA,
GL_UNSIGNED_BYTE, tex_rgba);
g_free(tex_rgba);
glUniform1i(r->disp_rndr.pvideo_tex_loc, 1);
glUniform2f(r->disp_rndr.pvideo_in_pos_loc, in_s, in_t);
glUniform4f(r->disp_rndr.pvideo_pos_loc,
out_x, out_y, out_width, out_height);
glUniform3f(r->disp_rndr.pvideo_scale_loc,
scale_x, scale_y, 1.0f / pg->surface_scale_factor);
}
static void render_display(NV2AState *d, SurfaceBinding *surface)
{
struct PGRAPHState *pg = &d->pgraph;
PGRAPHGLState *r = pg->gl_renderer_state;
unsigned int width, height;
uint32_t pline_offset, pstart_addr, pline_compare;
d->vga.get_resolution(&d->vga, (int*)&width, (int*)&height);
d->vga.get_offsets(&d->vga, &pline_offset, &pstart_addr, &pline_compare);
int line_offset = surface->pitch / pline_offset;
/* Adjust viewport height for interlaced mode, used only in 1080i */
if (d->vga.cr[NV_PRMCIO_INTERLACE_MODE] != NV_PRMCIO_INTERLACE_MODE_DISABLED) {
height *= 2;
}
pgraph_apply_scaling_factor(pg, &width, &height);
glBindFramebuffer(GL_FRAMEBUFFER, r->disp_rndr.fbo);
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, r->gl_display_buffer);
bool recreate = (
surface->fmt.gl_internal_format != r->gl_display_buffer_internal_format
|| width != r->gl_display_buffer_width
|| height != r->gl_display_buffer_height
|| surface->fmt.gl_format != r->gl_display_buffer_format
|| surface->fmt.gl_type != r->gl_display_buffer_type
);
if (recreate) {
/* XXX: There's apparently a bug in some Intel OpenGL drivers for
* Windows that will leak this texture when its orphaned after use in
* another context, apparently regardless of which thread it's created
* or released on.
*
* Driver: 27.20.100.8729 9/11/2020 W10 x64
* Track: https://community.intel.com/t5/Graphics/OpenGL-Windows-drivers-for-Intel-HD-630-leaking-GPU-memory-when/td-p/1274423
*/
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, 0);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
r->gl_display_buffer_internal_format = surface->fmt.gl_internal_format;
r->gl_display_buffer_width = width;
r->gl_display_buffer_height = height;
r->gl_display_buffer_format = surface->fmt.gl_format;
r->gl_display_buffer_type = surface->fmt.gl_type;
glTexImage2D(GL_TEXTURE_2D, 0,
r->gl_display_buffer_internal_format,
r->gl_display_buffer_width,
r->gl_display_buffer_height,
0,
r->gl_display_buffer_format,
r->gl_display_buffer_type,
NULL);
}
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, r->gl_display_buffer, 0);
GLenum DrawBuffers[1] = {GL_COLOR_ATTACHMENT0};
glDrawBuffers(1, DrawBuffers);
assert(glCheckFramebufferStatus(GL_FRAMEBUFFER) == GL_FRAMEBUFFER_COMPLETE);
glBindTexture(GL_TEXTURE_2D, surface->gl_buffer);
glBindVertexArray(r->disp_rndr.vao);
glBindBuffer(GL_ARRAY_BUFFER, r->disp_rndr.vbo);
glUseProgram(r->disp_rndr.prog);
glProgramUniform1i(r->disp_rndr.prog, r->disp_rndr.tex_loc, 0);
glUniform2f(r->disp_rndr.display_size_loc, width, height);
glUniform1f(r->disp_rndr.line_offset_loc, line_offset);
render_display_pvideo_overlay(d);
glViewport(0, 0, width, height);
glColorMask(true, true, true, true);
glDisable(GL_SCISSOR_TEST);
glDisable(GL_BLEND);
glDisable(GL_STENCIL_TEST);
glDisable(GL_CULL_FACE);
glDisable(GL_DEPTH_TEST);
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
glDrawArrays(GL_TRIANGLES, 0, 3);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
GL_TEXTURE_2D, 0, 0);
}
static void gl_fence(void)
{
GLsync fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
int result = glClientWaitSync(fence, GL_SYNC_FLUSH_COMMANDS_BIT,
(GLuint64)(5000000000));
assert(result == GL_CONDITION_SATISFIED || result == GL_ALREADY_SIGNALED);
glDeleteSync(fence);
}
void pgraph_gl_sync(NV2AState *d)
{
uint32_t pline_offset, pstart_addr, pline_compare;
d->vga.get_offsets(&d->vga, &pline_offset, &pstart_addr, &pline_compare);
SurfaceBinding *surface = pgraph_gl_surface_get_within(d, d->pcrtc.start + pline_offset);
if (surface == NULL) {
qemu_event_set(&d->pgraph.sync_complete);
return;
}
/* FIXME: Sanity check surface dimensions */
/* Wait for queued commands to complete */
pgraph_gl_upload_surface_data(d, surface, !tcg_enabled());
gl_fence();
assert(glGetError() == GL_NO_ERROR);
/* Render framebuffer in display context */
glo_set_current(g_nv2a_context_display);
render_display(d, surface);
gl_fence();
assert(glGetError() == GL_NO_ERROR);
/* Switch back to original context */
glo_set_current(g_nv2a_context_render);
qatomic_set(&d->pgraph.sync_pending, false);
qemu_event_set(&d->pgraph.sync_complete);
}
int pgraph_gl_get_framebuffer_surface(NV2AState *d)
{
PGRAPHState *pg = &d->pgraph;
PGRAPHGLState *r = pg->gl_renderer_state;
qemu_mutex_lock(&d->pfifo.lock);
// FIXME: Possible race condition with pgraph, consider lock
uint32_t pline_offset, pstart_addr, pline_compare;
d->vga.get_offsets(&d->vga, &pline_offset, &pstart_addr, &pline_compare);
SurfaceBinding *surface = pgraph_gl_surface_get_within(d, d->pcrtc.start + pline_offset);
if (surface == NULL || !surface->color) {
qemu_mutex_unlock(&d->pfifo.lock);
return 0;
}
assert(surface->color);
assert(surface->fmt.gl_attachment == GL_COLOR_ATTACHMENT0);
assert(surface->fmt.gl_format == GL_RGBA
|| surface->fmt.gl_format == GL_RGB
|| surface->fmt.gl_format == GL_BGR
|| surface->fmt.gl_format == GL_BGRA
);
surface->frame_time = pg->frame_time;
qemu_event_reset(&d->pgraph.sync_complete);
qatomic_set(&pg->sync_pending, true);
pfifo_kick(d);
qemu_mutex_unlock(&d->pfifo.lock);
qemu_event_wait(&d->pgraph.sync_complete);
return r->gl_display_buffer;
}

Some files were not shown because too many files have changed in this diff Show More