mirror of
https://github.com/izzy2lost/xemu.git
synced 2026-07-06 00:20:22 -07:00
nv2a: Add Vulkan renderer
This commit is contained in:
committed by
mborgerson
parent
e639e0cdb7
commit
a5385803db
+2
-2
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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.
|
||||
|
||||
Vendored
+3
@@ -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
@@ -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
|
||||
}
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
softmmu_ss.add([sdl, files(
|
||||
'gloffscreen_common.c',
|
||||
'gloffscreen_sdl.c',
|
||||
)])
|
||||
|
||||
# gloffscreen_sdl.o-cflags := $(SDL_CFLAGS)
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
@@ -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
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
Reference in New Issue
Block a user