/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2017 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #ifndef XENIA_GPU_REGISTERS_H_ #define XENIA_GPU_REGISTERS_H_ #include #include #include "xenia/base/assert.h" #include "xenia/gpu/xenos.h" // Most registers can be found from: // https://github.com/UDOOboard/Kernel_Unico/blob/master/drivers/mxc/amd-gpu/include/reg/yamato/14/yamato_registers.h // Some registers were added on Adreno specifically and are not referenced in // game .pdb files and never set by games. // Only 32-bit types (uint32_t, int32_t, float or enums with uint32_t / int32_t // as the underlying type) are allowed in the bit fields here, as Visual C++ // restarts packing when a field requires different alignment than the previous // one. namespace xe { namespace gpu { enum Register { #define XE_GPU_REGISTER(index, type, name) XE_GPU_REG_##name = index, #include "xenia/gpu/register_table.inc" #undef XE_GPU_REGISTER }; namespace reg { /******************************************************************************* ___ ___ _ _ _____ ___ ___ _ / __/ _ \| \| |_ _| _ \/ _ \| | | (_| (_) | .` | | | | / (_) | |__ \___\___/|_|\_| |_| |_|_\\___/|____| *******************************************************************************/ union alignas(uint32_t) COHER_STATUS_HOST { struct { uint32_t matching_contexts : 8; // +0 uint32_t rb_copy_dest_base_ena : 1; // +8 uint32_t dest_base_0_ena : 1; // +9 uint32_t dest_base_1_ena : 1; // +10 uint32_t dest_base_2_ena : 1; // +11 uint32_t dest_base_3_ena : 1; // +12 uint32_t dest_base_4_ena : 1; // +13 uint32_t dest_base_5_ena : 1; // +14 uint32_t dest_base_6_ena : 1; // +15 uint32_t dest_base_7_ena : 1; // +16 uint32_t : 7; // +17 uint32_t vc_action_ena : 1; // +24 uint32_t tc_action_ena : 1; // +25 uint32_t pglb_action_ena : 1; // +26 uint32_t : 4; // +27 uint32_t status : 1; // +31 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_COHER_STATUS_HOST; }; static_assert_size(COHER_STATUS_HOST, sizeof(uint32_t)); union alignas(uint32_t) WAIT_UNTIL { struct { uint32_t : 1; // +0 uint32_t wait_re_vsync : 1; // +1 uint32_t wait_fe_vsync : 1; // +2 uint32_t wait_vsync : 1; // +3 uint32_t wait_dsply_id0 : 1; // +4 uint32_t wait_dsply_id1 : 1; // +5 uint32_t wait_dsply_id2 : 1; // +6 uint32_t : 3; // +7 uint32_t wait_cmdfifo : 1; // +10 uint32_t : 3; // +11 uint32_t wait_2d_idle : 1; // +14 uint32_t wait_3d_idle : 1; // +15 uint32_t wait_2d_idleclean : 1; // +16 uint32_t wait_3d_idleclean : 1; // +17 uint32_t : 2; // +18 uint32_t cmdfifo_entries : 4; // +20 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_WAIT_UNTIL; }; static_assert_size(WAIT_UNTIL, sizeof(uint32_t)); /******************************************************************************* ___ ___ ___ _ _ ___ _ _ ___ ___ ___ / __| __/ _ \| | | | __| \| |/ __| __| _ \ \__ \ _| (_) | |_| | _|| .` | (__| _|| / |___/___\__\_\\___/|___|_|\_|\___|___|_|_\ *******************************************************************************/ union alignas(uint32_t) SQ_PROGRAM_CNTL { struct { // Note from a2xx.xml: // Only 0x3F worth of valid register values for VS_NUM_REG and PS_NUM_REG, // but high bit is set to indicate "0 registers used". // (Register count = (num_reg & 0x80) ? 0 : (num_reg + 1)) uint32_t vs_num_reg : 8; // +0 uint32_t ps_num_reg : 8; // +8 uint32_t vs_resource : 1; // +16 uint32_t ps_resource : 1; // +17 uint32_t param_gen : 1; // +18 uint32_t gen_index_pix : 1; // +19 uint32_t vs_export_count : 4; // +20 xenos::VertexShaderExportMode vs_export_mode : 3; // +24 uint32_t ps_export_mode : 4; // +27 uint32_t gen_index_vtx : 1; // +31 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_SQ_PROGRAM_CNTL; }; static_assert_size(SQ_PROGRAM_CNTL, sizeof(uint32_t)); union alignas(uint32_t) SQ_CONTEXT_MISC { struct { uint32_t inst_pred_optimize : 1; // +0 uint32_t sc_output_screen_xy : 1; // +1 xenos::SampleControl sc_sample_cntl : 2; // +2 uint32_t : 4; // +4 // Pixel shader interpolator (according to the XNA microcode compiler) index // to write pixel parameters to. So far have been able to find the following // usage: // * |XY| - position on screen (vPos - the XNA microcode compiler translates // ps_3_0 vPos directly to this, so at least in Direct3D 9 pixel center // mode, this contains 0, 1, 2, not 0.5, 1.5, 2.5). flto also said in the // Freedreno IRC that it's .0 even in OpenGL: // https://dri.freedesktop.org/~cbrill/dri-log/?channel=freedreno&date=2020-04-19 // (on Android, according to LG P705 GL_OES_get_program_binary // disassembly, gl_FragCoord.xy is |r0.xy| * c221.xy + c222.zw - haven't // been able to dump the constant values by exploiting a huge uniform // array, but flto says c222.zw contains tile offset plus 0.5). // * Sign bit of X - is front face (vFace), non-negative for front face, // negative for back face (used with `rcpc` in shaders to take signedness // of 0 into account in `cndge`). // * |ZW| - UV within a point sprite (sign meaning is unknown so far). uint32_t param_gen_pos : 8; // +8 uint32_t perfcounter_ref : 1; // +16 uint32_t yeild_optimize : 1; // +17 sic uint32_t tx_cache_sel : 1; // +18 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_SQ_CONTEXT_MISC; }; static_assert_size(SQ_CONTEXT_MISC, sizeof(uint32_t)); union alignas(uint32_t) SQ_INTERPOLATOR_CNTL { struct { uint32_t param_shade : 16; // +0 // SampleLocation bits - 0 for centroid, 1 for center, if // SQ_CONTEXT_MISC::sc_sample_cntl is kCentroidsAndCenters. uint32_t sampling_pattern : 16; // +16 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_SQ_INTERPOLATOR_CNTL; }; static_assert_size(SQ_INTERPOLATOR_CNTL, sizeof(uint32_t)); /******************************************************************************* __ _____ ___ _____ _____ __ \ \ / / __| _ \_ _| __\ \/ / \ V /| _|| / | | | _| > < \_/ |___|_|_\ |_| |___/_/\_\ ___ ___ ___ _ _ ___ ___ ___ _ _ _ ___ / __| _ \/ _ \| | | | _ \ __| _ \ /_\ | \| | \ | (_ | / (_) | |_| | _/ _|| / / _ \| .` | |) | \___|_|_\\___/ \___/|_| |___|_|_\ /_/ \_\_|\_|___/ _____ ___ ___ ___ ___ _ _ _ _____ ___ ___ |_ _| __/ __/ __| __| | | | /_\_ _/ _ \| _ \ | | | _|\__ \__ \ _|| |__| |__ / _ \| || (_) | / |_| |___|___/___/___|____|____/_/ \_\_| \___/|_|_\ *******************************************************************************/ union alignas(uint32_t) VGT_DMA_SIZE { struct { uint32_t num_words : 24; // +0 uint32_t : 6; // +24 xenos::Endian swap_mode : 2; // +30 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_VGT_DMA_SIZE; }; union alignas(uint32_t) VGT_DRAW_INITIATOR { // Different than on A2xx and R6xx/R7xx. struct { xenos::PrimitiveType prim_type : 6; // +0 xenos::SourceSelect source_select : 2; // +6 xenos::MajorMode major_mode : 2; // +8 uint32_t : 1; // +10 xenos::IndexFormat index_size : 1; // +11 uint32_t not_eop : 1; // +12 uint32_t : 3; // +13 uint32_t num_indices : 16; // +16 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_VGT_DRAW_INITIATOR; }; static_assert_size(VGT_DRAW_INITIATOR, sizeof(uint32_t)); // Unlike on R6xx (but closer to R5xx), and according to the Adreno 200 header, // the registers related to the vertex index are 24-bit. Vertex indices are // unsigned, and only the lower 24 bits of them are actually used by the GPU - // this has been verified on an Adreno 200 phone (LG Optimus L7) on OpenGL ES // using a GL_UNSIGNED_INT element array buffer with junk in the upper 8 bits // that had no effect on drawing. // The order of operations is primitive reset index checking -> offsetting -> // clamping. union alignas(uint32_t) VGT_MULTI_PRIM_IB_RESET_INDX { struct { // The upper 8 bits of the value from the index buffer are confirmed to be // ignored. So, though this specifically is untested (because // GL_PRIMITIVE_RESTART_FIXED_INDEX was added only in OpenGL ES 3.0, though // it behaves conceptually close to our expectations anyway - uses the // 0xFFFFFFFF restart index while GL_MAX_ELEMENT_INDEX may be 0xFFFFFF), // the restart index check likely only involves the lower 24 bit of the // vertex index - therefore, if reset_indx is 0xFFFFFF, likely 0xFFFFFF, // 0x1FFFFFF, 0xFFFFFFFF all cause primitive reset. uint32_t reset_indx : 24; }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_VGT_MULTI_PRIM_IB_RESET_INDX; }; static_assert_size(VGT_MULTI_PRIM_IB_RESET_INDX, sizeof(uint32_t)); union alignas(uint32_t) VGT_INDX_OFFSET { struct { // Unlike R5xx's VAP_INDEX_OFFSET, which is signed 25-bit, this is 24-bit - // and signedness doesn't matter as index calculations are done in 24-bit // integers, and ((0xFFFFFE + 3) & 0xFFFFFF) == 1 anyway, just like // ((0xFFFFFFFE + 3) & 0xFFFFFF) == 1 if we treated it as signed by // sign-extending on the host. Direct3D 9 just writes BaseVertexIndex as a // signed int32 to the entire register, but the upper 8 bits are ignored // anyway, and that has no effect on offsets that fit in 24 bits. uint32_t indx_offset : 24; }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_VGT_INDX_OFFSET; }; static_assert_size(VGT_INDX_OFFSET, sizeof(uint32_t)); union alignas(uint32_t) VGT_MIN_VTX_INDX { struct { uint32_t min_indx : 24; }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_VGT_MIN_VTX_INDX; }; static_assert_size(VGT_MIN_VTX_INDX, sizeof(uint32_t)); union alignas(uint32_t) VGT_MAX_VTX_INDX { struct { // Usually 0xFFFF or 0xFFFFFF. uint32_t max_indx : 24; }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_VGT_MAX_VTX_INDX; }; static_assert_size(VGT_MAX_VTX_INDX, sizeof(uint32_t)); union alignas(uint32_t) VGT_OUTPUT_PATH_CNTL { struct { xenos::VGTOutputPath path_select : 2; // +0 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_VGT_OUTPUT_PATH_CNTL; }; static_assert_size(VGT_OUTPUT_PATH_CNTL, sizeof(uint32_t)); union alignas(uint32_t) VGT_HOS_CNTL { struct { xenos::TessellationMode tess_mode : 2; // +0 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_VGT_HOS_CNTL; }; static_assert_size(VGT_HOS_CNTL, sizeof(uint32_t)); /******************************************************************************* ___ ___ ___ __ __ ___ _____ _____ _____ | _ \ _ \_ _| \/ |_ _|_ _|_ _\ \ / / __| | _/ /| || |\/| || | | | | | \ V /| _| |_| |_|_\___|_| |_|___| |_| |___| \_/ |___| _ ___ ___ ___ __ __ ___ _ ___ ___ /_\ / __/ __| __| \/ | _ ) | | __| _ \ / _ \\__ \__ \ _|| |\/| | _ \ |__| _|| / /_/ \_\___/___/___|_| |_|___/____|___|_|_\ *******************************************************************************/ union alignas(uint32_t) PA_SU_POINT_MINMAX { struct { // Radius, 12.4 fixed point. uint32_t min_size : 16; // +0 uint32_t max_size : 16; // +16 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_PA_SU_POINT_MINMAX; }; static_assert_size(PA_SU_POINT_MINMAX, sizeof(uint32_t)); union alignas(uint32_t) PA_SU_POINT_SIZE { struct { // 1/2 width or height, 12.4 fixed point. uint32_t height : 16; // +0 uint32_t width : 16; // +16 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_PA_SU_POINT_SIZE; }; static_assert_size(PA_SU_POINT_SIZE, sizeof(uint32_t)); // Setup Unit / Scanline Converter mode cntl union alignas(uint32_t) PA_SU_SC_MODE_CNTL { struct { uint32_t cull_front : 1; // +0 uint32_t cull_back : 1; // +1 // 0 - front is CCW, 1 - front is CW. uint32_t face : 1; // +2 // The game Fuse uses poly_mode 2 for triangles, which is "reserved" on R6xx // and not defined on Adreno 2xx, but polymode_front/back_ptype are 0 // (points) in this case in Fuse, which should not be respected for // non-kDualMode as the game wants to draw filled triangles. xenos::PolygonModeEnable poly_mode : 2; // +3 xenos::PolygonType polymode_front_ptype : 3; // +5 xenos::PolygonType polymode_back_ptype : 3; // +8 uint32_t poly_offset_front_enable : 1; // +11 uint32_t poly_offset_back_enable : 1; // +12 uint32_t poly_offset_para_enable : 1; // +13 uint32_t : 1; // +14 uint32_t msaa_enable : 1; // +15 uint32_t vtx_window_offset_enable : 1; // +16 // LINE_STIPPLE_ENABLE was added on Adreno. uint32_t : 2; // +17 uint32_t provoking_vtx_last : 1; // +19 uint32_t persp_corr_dis : 1; // +20 uint32_t multi_prim_ib_ena : 1; // +21 uint32_t : 1; // +22 uint32_t quad_order_enable : 1; // +23 uint32_t sc_one_quad_per_clock : 1; // +24 // WAIT_RB_IDLE_ALL_TRI and WAIT_RB_IDLE_FIRST_TRI_NEW_STATE were added on // Adreno. }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_PA_SU_SC_MODE_CNTL; }; static_assert_size(PA_SU_SC_MODE_CNTL, sizeof(uint32_t)); // Setup Unit Vertex Control union alignas(uint32_t) PA_SU_VTX_CNTL { struct { uint32_t pix_center : 1; // +0 1 = half pixel offset (OpenGL). uint32_t round_mode : 2; // +1 uint32_t quant_mode : 3; // +3 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_PA_SU_VTX_CNTL; }; static_assert_size(PA_SU_VTX_CNTL, sizeof(uint32_t)); union alignas(uint32_t) PA_SC_MPASS_PS_CNTL { struct { uint32_t mpass_pix_vec_per_pass : 20; // +0 uint32_t : 11; // +20 uint32_t mpass_ps_ena : 1; // +31 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_PA_SC_MPASS_PS_CNTL; }; static_assert_size(PA_SC_MPASS_PS_CNTL, sizeof(uint32_t)); // Scanline converter viz query, used by D3D for gpu side conditional rendering union alignas(uint32_t) PA_SC_VIZ_QUERY { struct { // the visibility of draws should be evaluated uint32_t viz_query_ena : 1; // +0 uint32_t viz_query_id : 6; // +1 // discard geometry after test (but use for testing) uint32_t kill_pix_post_hi_z : 1; // +7 // not used with d3d uint32_t kill_pix_post_detail_mask : 1; // +8 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_PA_SC_VIZ_QUERY; }; static_assert_size(PA_SC_VIZ_QUERY, sizeof(uint32_t)); // Clipper clip control union alignas(uint32_t) PA_CL_CLIP_CNTL { struct { uint32_t ucp_ena_0 : 1; // +0 uint32_t ucp_ena_1 : 1; // +1 uint32_t ucp_ena_2 : 1; // +2 uint32_t ucp_ena_3 : 1; // +3 uint32_t ucp_ena_4 : 1; // +4 uint32_t ucp_ena_5 : 1; // +5 uint32_t : 8; // +6 uint32_t ps_ucp_mode : 2; // +14 uint32_t clip_disable : 1; // +16 uint32_t ucp_cull_only_ena : 1; // +17 uint32_t boundary_edge_flag_ena : 1; // +18 uint32_t dx_clip_space_def : 1; // +19 uint32_t dis_clip_err_detect : 1; // +20 uint32_t vtx_kill_or : 1; // +21 uint32_t xy_nan_retain : 1; // +22 uint32_t z_nan_retain : 1; // +23 uint32_t w_nan_retain : 1; // +24 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_PA_CL_CLIP_CNTL; }; static_assert_size(PA_CL_CLIP_CNTL, sizeof(uint32_t)); // Viewport transform engine control union alignas(uint32_t) PA_CL_VTE_CNTL { struct { uint32_t vport_x_scale_ena : 1; // +0 uint32_t vport_x_offset_ena : 1; // +1 uint32_t vport_y_scale_ena : 1; // +2 uint32_t vport_y_offset_ena : 1; // +3 uint32_t vport_z_scale_ena : 1; // +4 uint32_t vport_z_offset_ena : 1; // +5 uint32_t : 2; // +6 uint32_t vtx_xy_fmt : 1; // +8 uint32_t vtx_z_fmt : 1; // +9 uint32_t vtx_w0_fmt : 1; // +10 uint32_t perfcounter_ref : 1; // +11 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_PA_CL_VTE_CNTL; }; static_assert_size(PA_CL_VTE_CNTL, sizeof(uint32_t)); union alignas(uint32_t) PA_SC_SCREEN_SCISSOR_TL { struct { int32_t tl_x : 15; // +0 uint32_t : 1; // +15 int32_t tl_y : 15; // +16 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_PA_SC_SCREEN_SCISSOR_TL; }; static_assert_size(PA_SC_SCREEN_SCISSOR_TL, sizeof(uint32_t)); union alignas(uint32_t) PA_SC_SCREEN_SCISSOR_BR { struct { int32_t br_x : 15; // +0 uint32_t : 1; // +15 int32_t br_y : 15; // +16 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_PA_SC_SCREEN_SCISSOR_BR; }; static_assert_size(PA_SC_SCREEN_SCISSOR_BR, sizeof(uint32_t)); union alignas(uint32_t) PA_SC_WINDOW_OFFSET { struct { int32_t window_x_offset : 15; // +0 uint32_t : 1; // +15 int32_t window_y_offset : 15; // +16 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_PA_SC_WINDOW_OFFSET; }; static_assert_size(PA_SC_WINDOW_OFFSET, sizeof(uint32_t)); union alignas(uint32_t) PA_SC_WINDOW_SCISSOR_TL { struct { uint32_t tl_x : 14; // +0 uint32_t : 2; // +14 uint32_t tl_y : 14; // +16 uint32_t : 1; // +30 uint32_t window_offset_disable : 1; // +31 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_PA_SC_WINDOW_SCISSOR_TL; }; static_assert_size(PA_SC_WINDOW_SCISSOR_TL, sizeof(uint32_t)); union alignas(uint32_t) PA_SC_WINDOW_SCISSOR_BR { struct { uint32_t br_x : 14; // +0 uint32_t : 2; // +14 uint32_t br_y : 14; // +16 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_PA_SC_WINDOW_SCISSOR_BR; }; static_assert_size(PA_SC_WINDOW_SCISSOR_BR, sizeof(uint32_t)); /******************************************************************************* ___ ___ | _ \ _ ) | / _ \ |_|_\___/ *******************************************************************************/ union alignas(uint32_t) RB_MODECONTROL { struct { xenos::ModeControl edram_mode : 3; // +0 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_RB_MODECONTROL; }; static_assert_size(RB_MODECONTROL, sizeof(uint32_t)); union alignas(uint32_t) RB_SURFACE_INFO { struct { uint32_t surface_pitch : 14; // +0 in pixels. uint32_t : 2; // +14 xenos::MsaaSamples msaa_samples : 2; // +16 uint32_t hiz_pitch : 14; // +18 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_RB_SURFACE_INFO; }; static_assert_size(RB_SURFACE_INFO, sizeof(uint32_t)); union alignas(uint32_t) RB_COLORCONTROL { struct { xenos::CompareFunction alpha_func : 3; // +0 uint32_t alpha_test_enable : 1; // +3 uint32_t alpha_to_mask_enable : 1; // +4 // Everything in between was added on Adreno. uint32_t : 19; // +5 // According to tests on an Adreno 200 device (LG Optimus L7), done by // drawing 0.5x0.5 rectangles in different corners of four pixels in a quad // to a multisampled GLSurfaceView, the coverage mask is the following for 4 // samples: // 0.25) [0.25, 0.5) [0.5, 0.75) [0.75, 1) [1 // -- -- -- -- -- // | | | | | #| |##| |##| // | | |# | |# | |# | |##| // -- -- -- -- -- // (gl_FragCoord.y near 0 in the top, near 1 in the bottom here - D3D-like.) // For 2 samples, the top sample (closer to gl_FragCoord.y 0) is covered // when alpha is in [0.5, 1), the bottom sample is covered when the alpha is // [1. With these thresholds, however, in Red Dead Redemption, almost all // distant trees are transparent, this is asymmetric - fully transparent for // a quarter of the range (or even half of the range for 2x and almost the // entire range for 1x), but fully opaque only in one value. // Though, 2, 2, 2, 2 offset values are commonly used for undithered alpha // to coverage (in games such as Red Dead Redemption, and overall in AMD // driver implementations) - it appears that 2, 2, 2, 2 offsets are supposed // to make this symmetric. // Both Red Dead Redemption and RADV (which used AMDVLK as a reference) use // 3, 1, 0, 2 offsets for dithered alpha to mask. // https://gitlab.freedesktop.org/nchery/mesa/commit/8a52e4cc4fad4f1c75acc0badd624778f9dfe202 // It appears that the offsets lower the thresholds by (offset / 4 / // sample count). That's consistent with both 2, 2, 2, 2 making the test // symmetric and 0, 0, 0, 0 (forgetting to set the offset values) resulting // in what the official Adreno 200 driver for Android (which is pretty buggy // overall) produces. // According to Evergreen register reference: // - offset0 is for pixel (0, 0) in each quad. // - offset1 is for pixel (0, 1) in each quad. // - offset2 is for pixel (1, 0) in each quad. // - offset3 is for pixel (1, 1) in each quad. uint32_t alpha_to_mask_offset0 : 2; // +24 uint32_t alpha_to_mask_offset1 : 2; // +26 uint32_t alpha_to_mask_offset2 : 2; // +28 uint32_t alpha_to_mask_offset3 : 2; // +30 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_RB_COLORCONTROL; }; static_assert_size(RB_COLORCONTROL, sizeof(uint32_t)); union alignas(uint32_t) RB_COLOR_INFO { struct { uint32_t color_base : 12; // +0 in tiles. uint32_t : 4; // +12 xenos::ColorRenderTargetFormat color_format : 4; // +16 int32_t color_exp_bias : 6; // +20 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_RB_COLOR_INFO; // RB_COLOR[1-3]_INFO also use this format. static const Register rt_register_indices[4]; }; static_assert_size(RB_COLOR_INFO, sizeof(uint32_t)); union alignas(uint32_t) RB_COLOR_MASK { struct { uint32_t write_red0 : 1; // +0 uint32_t write_green0 : 1; // +1 uint32_t write_blue0 : 1; // +2 uint32_t write_alpha0 : 1; // +3 uint32_t write_red1 : 1; // +4 uint32_t write_green1 : 1; // +5 uint32_t write_blue1 : 1; // +6 uint32_t write_alpha1 : 1; // +7 uint32_t write_red2 : 1; // +8 uint32_t write_green2 : 1; // +9 uint32_t write_blue2 : 1; // +10 uint32_t write_alpha2 : 1; // +11 uint32_t write_red3 : 1; // +12 uint32_t write_green3 : 1; // +13 uint32_t write_blue3 : 1; // +14 uint32_t write_alpha3 : 1; // +15 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_RB_COLOR_MASK; }; static_assert_size(RB_COLOR_MASK, sizeof(uint32_t)); union alignas(uint32_t) RB_BLENDCONTROL { struct { xenos::BlendFactor color_srcblend : 5; // +0 xenos::BlendOp color_comb_fcn : 3; // +5 xenos::BlendFactor color_destblend : 5; // +8 uint32_t : 3; // +13 xenos::BlendFactor alpha_srcblend : 5; // +16 xenos::BlendOp alpha_comb_fcn : 3; // +21 xenos::BlendFactor alpha_destblend : 5; // +24 // BLEND_FORCE_ENABLE and BLEND_FORCE were added on Adreno. }; uint32_t value; // RB_BLENDCONTROL[0-3] use this format. static constexpr Register register_index = XE_GPU_REG_RB_BLENDCONTROL0; static const Register rt_register_indices[4]; }; static_assert_size(RB_BLENDCONTROL, sizeof(uint32_t)); union alignas(uint32_t) RB_DEPTHCONTROL { struct { uint32_t stencil_enable : 1; // +0 uint32_t z_enable : 1; // +1 uint32_t z_write_enable : 1; // +2 // EARLY_Z_ENABLE was added on Adreno. uint32_t : 1; // +3 xenos::CompareFunction zfunc : 3; // +4 uint32_t backface_enable : 1; // +7 xenos::CompareFunction stencilfunc : 3; // +8 xenos::StencilOp stencilfail : 3; // +11 xenos::StencilOp stencilzpass : 3; // +14 xenos::StencilOp stencilzfail : 3; // +17 xenos::CompareFunction stencilfunc_bf : 3; // +20 xenos::StencilOp stencilfail_bf : 3; // +23 xenos::StencilOp stencilzpass_bf : 3; // +26 xenos::StencilOp stencilzfail_bf : 3; // +29 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_RB_DEPTHCONTROL; }; static_assert_size(RB_DEPTHCONTROL, sizeof(uint32_t)); union alignas(uint32_t) RB_STENCILREFMASK { struct { uint32_t stencilref : 8; // +0 uint32_t stencilmask : 8; // +8 uint32_t stencilwritemask : 8; // +16 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_RB_STENCILREFMASK; // RB_STENCILREFMASK_BF also uses this format. }; static_assert_size(RB_STENCILREFMASK, sizeof(uint32_t)); union alignas(uint32_t) RB_DEPTH_INFO { struct { uint32_t depth_base : 12; // +0 in tiles. uint32_t : 4; // +12 xenos::DepthRenderTargetFormat depth_format : 1; // +16 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_RB_DEPTH_INFO; }; static_assert_size(RB_DEPTH_INFO, sizeof(uint32_t)); // Copy registers are very different than on Adreno. union alignas(uint32_t) RB_COPY_CONTROL { struct { uint32_t copy_src_select : 3; // +0 Depth is 4. uint32_t : 1; // +3 xenos::CopySampleSelect copy_sample_select : 3; // +4 uint32_t : 1; // +7 uint32_t color_clear_enable : 1; // +8 uint32_t depth_clear_enable : 1; // +9 uint32_t : 10; // +10 xenos::CopyCommand copy_command : 2; // +20 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_RB_COPY_CONTROL; }; static_assert_size(RB_COPY_CONTROL, sizeof(uint32_t)); union alignas(uint32_t) RB_COPY_DEST_INFO { struct { xenos::Endian128 copy_dest_endian : 3; // +0 uint32_t copy_dest_array : 1; // +3 uint32_t copy_dest_slice : 3; // +4 xenos::ColorFormat copy_dest_format : 6; // +7 uint32_t copy_dest_number : 3; // +13 int32_t copy_dest_exp_bias : 6; // +16 uint32_t : 2; // +22 uint32_t copy_dest_swap : 1; // +24 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_RB_COPY_DEST_INFO; }; static_assert_size(RB_COPY_DEST_INFO, sizeof(uint32_t)); union alignas(uint32_t) RB_COPY_DEST_PITCH { struct { uint32_t copy_dest_pitch : 14; // +0 uint32_t : 2; // +14 uint32_t copy_dest_height : 14; // +16 }; uint32_t value; static constexpr Register register_index = XE_GPU_REG_RB_COPY_DEST_PITCH; }; static_assert_size(RB_COPY_DEST_PITCH, sizeof(uint32_t)); } // namespace reg } // namespace gpu } // namespace xe #endif // XENIA_GPU_REGISTERS_H_