/** ****************************************************************************** * Xenia : Xbox 360 Emulator Research Project * ****************************************************************************** * Copyright 2020 Ben Vanik. All rights reserved. * * Released under the BSD license - see LICENSE in the root for more details. * ****************************************************************************** */ #ifndef XENIA_GPU_DRAW_UTIL_H_ #define XENIA_GPU_DRAW_UTIL_H_ #include #include #include "xenia/base/assert.h" #include "xenia/gpu/register_file.h" #include "xenia/gpu/registers.h" #include "xenia/gpu/trace_writer.h" #include "xenia/gpu/xenos.h" #include "xenia/memory.h" namespace xe { namespace gpu { namespace draw_util { // For estimating coverage extents from vertices. This may give results that are // different than what the host GPU will actually draw (this is the reference // conversion with 1/2 ULP accuracy, but Direct3D 11 permits 0.6 ULP tolerance // in floating point to fixed point conversion), but is enough to tie-break // vertices at pixel centers (due to the half-pixel offset applied to integer // coordinates incorrectly, for instance) with some error tolerance near 0.5, // for use with the top-left rasterization rule later. int32_t FloatToD3D11Fixed16p8(float f32); // To avoid passing values that the shader won't understand (even though // Direct3D 9 shouldn't pass them anyway). xenos::CopySampleSelect SanitizeCopySampleSelect( xenos::CopySampleSelect copy_sample_select, xenos::MsaaSamples msaa_samples, bool is_depth); // Packed structures are small and can be passed to the shaders in root/push // constants. union ResolveEdramPackedInfo { struct { // With offset to the 160x32 region that local_x/y_div_8 are relative to, // and with 32bpp/64bpp taken into account. uint32_t pitch_tiles : xenos::kEdramPitchTilesBits; xenos::MsaaSamples msaa_samples : xenos::kMsaaSamplesBits; uint32_t is_depth : 1; uint32_t base_tiles : xenos::kEdramBaseTilesBits; uint32_t format : xenos::kRenderTargetFormatBits; uint32_t format_is_64bpp : 1; // Whether to take the value of column/row 1 for column/row 0, to reduce // the impact of the half-pixel offset with resolution scaling. uint32_t duplicate_second_pixel : 1; }; uint32_t packed; }; static_assert(sizeof(ResolveEdramPackedInfo) <= sizeof(uint32_t), "ResolveEdramPackedInfo must be packable in uint32_t"); union ResolveAddressPackedInfo { struct { // 160x32 is divisible by both the EDRAM tile size (80x16 samples, but for // simplicity, this is in pixels) and the texture tile size (32x32), so // the X and Y offsets can be packed in a very small number of bits (also // taking 8x8 granularity into account) if the offset of the 160x32 region // itself, and the offset of the texture tile, are pre-added to the bases. // In the EDRAM source, the whole offset is relative to the base. // In the texture, & 31 of the offset is relative to the base (the base is // adjusted to 32x32 tiles). // 0...19 for 0...152. uint32_t local_x_div_8 : 5; // 0...3 for 0...24. uint32_t local_y_div_8 : 2; // May be zero if the original rectangle was somehow specified in a // totally broken way - in this case, the resolve must be dropped. uint32_t width_div_8 : xenos::kResolveSizeBits - xenos::kResolveAlignmentPixelsLog2; uint32_t height_div_8 : xenos::kResolveSizeBits - xenos::kResolveAlignmentPixelsLog2; xenos::CopySampleSelect copy_sample_select : 3; }; uint32_t packed; }; static_assert(sizeof(ResolveAddressPackedInfo) <= sizeof(uint32_t), "ResolveAddressPackedInfo must be packable in uint32_t"); // For backends with Shader Model 5-like compute, host shaders to use to perform // copying in resolve operations. enum class ResolveCopyShaderIndex { kFast32bpp1x2xMSAA, kFast32bpp4xMSAA, kFast32bpp2xRes, kFast64bpp1x2xMSAA, kFast64bpp4xMSAA, kFast64bpp2xRes, kFull8bpp, kFull8bpp2xRes, kFull16bpp, kFull16bpp2xRes, kFull32bpp, kFull32bpp2xRes, kFull64bpp, kFull64bpp2xRes, kFull128bpp, kFull128bpp2xRes, kCount, kUnknown = kCount, }; struct ResolveCopyShaderInfo { // Debug name of the pipeline state object with this shader. const char* debug_name; // Only need to load this shader if the emulator resolution scale == this // value. uint32_t resolution_scale; // Whether the EDRAM source needs be bound as a raw buffer (ByteAddressBuffer // in Direct3D) since it can load different numbers of 32-bit values at once // on some hardware. If the host API doesn't support raw buffers, a typed // buffer with source_bpe_log2-byte elements needs to be used instead. bool source_is_raw; // Log2 of bytes per element of the type of the EDRAM buffer bound to the // shader (at least 2). uint32_t source_bpe_log2; // Log2 of bytes per element of the type of the destination buffer bound to // the shader (at least 2 because of Nvidia's 128 megatexel limit that // prevents binding the entire shared memory buffer with smaller element // sizes). uint32_t dest_bpe_log2; // Log2 of number of pixels in a single thread group along X and Y. 64 threads // per group preferred (GCN lane count). uint32_t group_size_x_log2, group_size_y_log2; }; extern const ResolveCopyShaderInfo resolve_copy_shader_info[size_t(ResolveCopyShaderIndex::kCount)]; struct ResolveCopyShaderConstants { // When the destination base is not needed (not binding the entire shared // memory buffer - with resoluion scaling, for instance), only the // DestRelative part may be passed to the shader to use less constants. struct DestRelative { ResolveEdramPackedInfo edram_info; ResolveAddressPackedInfo address_info; reg::RB_COPY_DEST_INFO dest_info; reg::RB_COPY_DEST_PITCH dest_pitch; }; DestRelative dest_relative; uint32_t dest_base; }; struct ResolveClearShaderConstants { // rt_specific is different for color and depth, the rest is the same and can // be preserved in the root bindings when going from depth to color. struct RenderTargetSpecific { uint32_t clear_value[2]; ResolveEdramPackedInfo edram_info; }; RenderTargetSpecific rt_specific; ResolveAddressPackedInfo address_info; }; struct ResolveInfo { reg::RB_COPY_CONTROL rb_copy_control; // color_edram_info and depth_edram_info are set up if copying or clearing // color and depth respectively, according to RB_COPY_CONTROL. ResolveEdramPackedInfo color_edram_info; ResolveEdramPackedInfo depth_edram_info; ResolveAddressPackedInfo address; reg::RB_COPY_DEST_INFO rb_copy_dest_info; reg::RB_COPY_DEST_PITCH rb_copy_dest_pitch; // Memory range that will potentially be modified by copying, with // address.local_x/y_div_8 & 31 being the origin relative to it. uint32_t copy_dest_base; // May be zero if something is wrong with the destination, in this case, // clearing may still be done, but copying must be dropped. uint32_t copy_dest_length; // The clear shaders always write to a uint4 view of EDRAM. uint32_t rb_depth_clear; uint32_t rb_color_clear; uint32_t rb_color_clear_lo; bool IsCopyingDepth() const { return rb_copy_control.copy_src_select >= xenos::kMaxColorRenderTargets; } ResolveCopyShaderIndex GetCopyShader( uint32_t resolution_scale, ResolveCopyShaderConstants& constants_out, uint32_t& group_count_x_out, uint32_t& group_count_y_out) const; bool IsClearingDepth() const { return rb_copy_control.depth_clear_enable != 0; } bool IsClearingColor() const { return !IsCopyingDepth() && rb_copy_control.color_clear_enable != 0; } void GetDepthClearShaderConstants( bool has_float32_copy, ResolveClearShaderConstants& constants_out) const { assert_true(IsClearingDepth()); constants_out.rt_specific.clear_value[0] = rb_depth_clear; if (has_float32_copy) { float depth32; uint32_t depth24 = rb_depth_clear >> 8; if (xenos::DepthRenderTargetFormat(depth_edram_info.format) == xenos::DepthRenderTargetFormat::kD24S8) { depth32 = depth24 * float(1.0f / 16777215.0f); } else { depth32 = xenos::Float20e4To32(depth24); } constants_out.rt_specific.clear_value[1] = *reinterpret_cast(&depth32); } else { constants_out.rt_specific.clear_value[1] = rb_depth_clear; } constants_out.rt_specific.edram_info = depth_edram_info; constants_out.address_info = address; } void GetColorClearShaderConstants( ResolveClearShaderConstants& constants_out) const { assert_true(IsClearingColor()); // Not doing -32...32 to -1...1 clamping here as a hack for k_16_16 and // k_16_16_16_16 blending emulation when using traditional host render // targets as it would be inconsistent with the usual way of clearing with a // quad. // TODO(Triang3l): Check which 32-bit portion is in which register. constants_out.rt_specific.clear_value[0] = rb_color_clear; constants_out.rt_specific.clear_value[1] = rb_color_clear_lo; constants_out.rt_specific.edram_info = color_edram_info; constants_out.address_info = address; } std::pair GetClearShaderGroupCount() const { // 8 guest MSAA samples per invocation. uint32_t width_samples_div_8 = address.width_div_8; uint32_t height_samples_div_8 = address.height_div_8; xenos::MsaaSamples samples = IsCopyingDepth() ? depth_edram_info.msaa_samples : color_edram_info.msaa_samples; if (samples >= xenos::MsaaSamples::k2X) { height_samples_div_8 <<= 1; if (samples >= xenos::MsaaSamples::k4X) { width_samples_div_8 <<= 1; } } return std::make_pair((width_samples_div_8 + uint32_t(7)) >> 3, height_samples_div_8); } }; // Returns false if there was an error obtaining the info making it totally // invalid. edram_16_as_minus_1_to_1 is false if 16_16 and 16_16_16_16 color // render target formats are properly emulated as -32...32, true if emulated as // snorm, with range limited to -1...1, but with correct blending within that // range. bool GetResolveInfo(const RegisterFile& regs, const Memory& memory, TraceWriter& trace_writer, uint32_t resolution_scale, bool edram_16_as_minus_1_to_1, ResolveInfo& info_out); // Taking user configuration - stretching or letterboxing, overscan region to // crop to fill while maintaining the aspect ratio - into account, returns the // area where the frame should be presented in the host window. void GetPresentArea(uint32_t source_width, uint32_t source_height, uint32_t window_width, uint32_t window_height, int32_t& target_x_out, int32_t& target_y_out, uint32_t& target_width_out, uint32_t& target_height_out); } // namespace draw_util } // namespace gpu } // namespace xe #endif // XENIA_GPU_DRAW_UTIL_H_