Merge branch 'master' of https://github.com/xenia-project/xenia into canary_experimental
This commit is contained in:
@@ -2171,7 +2171,9 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
|
||||
// TODO(Triang3l): Tessellation, geometry-type-specific vertex shader,
|
||||
// vertex shader as compute.
|
||||
if (primitive_processing_result.host_vertex_shader_type !=
|
||||
Shader::HostVertexShaderType::kVertex) {
|
||||
Shader::HostVertexShaderType::kVertex &&
|
||||
primitive_processing_result.host_vertex_shader_type !=
|
||||
Shader::HostVertexShaderType::kPointListAsTriangleStrip) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -2179,7 +2181,7 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
|
||||
vertex_shader_modification =
|
||||
pipeline_cache_->GetCurrentVertexShaderModification(
|
||||
*vertex_shader, primitive_processing_result.host_vertex_shader_type,
|
||||
interpolator_mask);
|
||||
interpolator_mask, ps_param_gen_pos != UINT32_MAX);
|
||||
pixel_shader_modification =
|
||||
pixel_shader ? pipeline_cache_->GetCurrentPixelShaderModification(
|
||||
*pixel_shader, interpolator_mask, ps_param_gen_pos)
|
||||
@@ -2348,6 +2350,7 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
|
||||
}
|
||||
|
||||
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
|
||||
const VkPhysicalDeviceFeatures& device_features = provider.device_features();
|
||||
const VkPhysicalDeviceLimits& device_limits =
|
||||
provider.device_properties().limits;
|
||||
|
||||
@@ -2382,11 +2385,23 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
|
||||
UpdateDynamicState(viewport_info, primitive_polygonal,
|
||||
normalized_depth_control);
|
||||
|
||||
auto vgt_draw_initiator = regs.Get<reg::VGT_DRAW_INITIATOR>();
|
||||
|
||||
// Whether to load the guest 32-bit (usually big-endian) vertex index
|
||||
// indirectly in the vertex shader if full 32-bit indices are not supported by
|
||||
// the host.
|
||||
bool shader_32bit_index_dma =
|
||||
!device_features.fullDrawIndexUint32 &&
|
||||
primitive_processing_result.index_buffer_type ==
|
||||
PrimitiveProcessor::ProcessedIndexBufferType::kGuestDMA &&
|
||||
vgt_draw_initiator.index_size == xenos::IndexFormat::kInt32 &&
|
||||
primitive_processing_result.host_vertex_shader_type ==
|
||||
Shader::HostVertexShaderType::kVertex;
|
||||
|
||||
// Update system constants before uploading them.
|
||||
bool vertex_shader_index_load;
|
||||
UpdateSystemConstantValues(primitive_polygonal, primitive_processing_result,
|
||||
viewport_info, used_texture_mask,
|
||||
vertex_shader_index_load);
|
||||
shader_32bit_index_dma, viewport_info,
|
||||
used_texture_mask);
|
||||
|
||||
// Update uniform buffers and descriptor sets after binding the pipeline with
|
||||
// the new layout.
|
||||
@@ -2453,13 +2468,13 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
|
||||
// Draw.
|
||||
if (primitive_processing_result.index_buffer_type ==
|
||||
PrimitiveProcessor::ProcessedIndexBufferType::kNone ||
|
||||
vertex_shader_index_load) {
|
||||
shader_32bit_index_dma) {
|
||||
deferred_command_buffer_.CmdVkDraw(
|
||||
primitive_processing_result.host_draw_vertex_count, 1, 0, 0);
|
||||
} else {
|
||||
std::pair<VkBuffer, VkDeviceSize> index_buffer;
|
||||
switch (primitive_processing_result.index_buffer_type) {
|
||||
case PrimitiveProcessor::ProcessedIndexBufferType::kGuest:
|
||||
case PrimitiveProcessor::ProcessedIndexBufferType::kGuestDMA:
|
||||
index_buffer.first = shared_memory_->buffer();
|
||||
index_buffer.second = primitive_processing_result.guest_index_base;
|
||||
break;
|
||||
@@ -2467,7 +2482,8 @@ bool VulkanCommandProcessor::IssueDraw(xenos::PrimitiveType prim_type,
|
||||
index_buffer = primitive_processor_->GetConvertedIndexBuffer(
|
||||
primitive_processing_result.host_index_buffer_handle);
|
||||
break;
|
||||
case PrimitiveProcessor::ProcessedIndexBufferType::kHostBuiltin:
|
||||
case PrimitiveProcessor::ProcessedIndexBufferType::kHostBuiltinForAuto:
|
||||
case PrimitiveProcessor::ProcessedIndexBufferType::kHostBuiltinForDMA:
|
||||
index_buffer = primitive_processor_->GetBuiltinIndexBuffer(
|
||||
primitive_processing_result.host_index_buffer_handle);
|
||||
break;
|
||||
@@ -3342,8 +3358,8 @@ void VulkanCommandProcessor::UpdateDynamicState(
|
||||
void VulkanCommandProcessor::UpdateSystemConstantValues(
|
||||
bool primitive_polygonal,
|
||||
const PrimitiveProcessor::ProcessingResult& primitive_processing_result,
|
||||
const draw_util::ViewportInfo& viewport_info, uint32_t used_texture_mask,
|
||||
bool& vertex_shader_index_load_out) {
|
||||
bool shader_32bit_index_dma, const draw_util::ViewportInfo& viewport_info,
|
||||
uint32_t used_texture_mask) {
|
||||
#if XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
SCOPE_profile_cpu_f("gpu");
|
||||
#endif // XE_UI_VULKAN_FINE_GRAINED_DRAW_SCOPES
|
||||
@@ -3367,51 +3383,17 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
|
||||
// Flags.
|
||||
uint32_t flags = 0;
|
||||
// Vertex index shader loading.
|
||||
bool vertex_shader_index_load = false;
|
||||
// Only for ProcessedIndexBufferType kGuest since kHostConverted indices may
|
||||
// be not loaded into the GPU memory (only read on the CPU), though
|
||||
// kHostConverted must never be used for point lists and rectangle lists
|
||||
// without geometry shaders anyway. For regular 32-bit index fetching without
|
||||
// fullDrawIndexUint32, kHostConverted indices are already byte-swapped and
|
||||
// truncated to 24 bits, so indirect fetch is not needed.
|
||||
if (shader_32bit_index_dma) {
|
||||
flags |= SpirvShaderTranslator::kSysFlag_VertexIndexLoad;
|
||||
}
|
||||
if (primitive_processing_result.index_buffer_type ==
|
||||
PrimitiveProcessor::ProcessedIndexBufferType::kGuest) {
|
||||
switch (primitive_processing_result.host_vertex_shader_type) {
|
||||
case Shader::HostVertexShaderType::kVertex: {
|
||||
// For guest (usually big-endian) 32-bit indices when they're not
|
||||
// supported by the device.
|
||||
if (vgt_draw_initiator.index_size == xenos::IndexFormat::kInt32) {
|
||||
const ui::vulkan::VulkanProvider& provider = GetVulkanProvider();
|
||||
const VkPhysicalDeviceFeatures& device_features =
|
||||
provider.device_features();
|
||||
if (!device_features.fullDrawIndexUint32) {
|
||||
vertex_shader_index_load = true;
|
||||
flags |= SpirvShaderTranslator::kSysFlag_VertexIndexLoad;
|
||||
}
|
||||
}
|
||||
} break;
|
||||
// kMemexportCompute never comes out of the PrimitiveProcessor, as
|
||||
// memexport compute shaders are executed alongside their vertex
|
||||
// counterparts, since they may still result in drawing.
|
||||
case Shader::HostVertexShaderType::kPointListAsTriangleStrip:
|
||||
case Shader::HostVertexShaderType::kRectangleListAsTriangleStrip: {
|
||||
// Always loading the guest index buffer indirectly if it's used, as
|
||||
// host indexing contains a part needed specifically for the host for
|
||||
// the construction of the primitive - host vertices don't map 1:1 to
|
||||
// guest ones.
|
||||
vertex_shader_index_load = true;
|
||||
flags |=
|
||||
SpirvShaderTranslator::kSysFlag_ComputeOrPrimitiveVertexIndexLoad;
|
||||
if (vgt_draw_initiator.index_size == xenos::IndexFormat::kInt32) {
|
||||
flags |= SpirvShaderTranslator ::
|
||||
kSysFlag_ComputeOrPrimitiveVertexIndexLoad32Bit;
|
||||
}
|
||||
} break;
|
||||
default:
|
||||
break;
|
||||
PrimitiveProcessor::ProcessedIndexBufferType::kHostBuiltinForDMA) {
|
||||
flags |= SpirvShaderTranslator::kSysFlag_ComputeOrPrimitiveVertexIndexLoad;
|
||||
if (vgt_draw_initiator.index_size == xenos::IndexFormat::kInt32) {
|
||||
flags |= SpirvShaderTranslator ::
|
||||
kSysFlag_ComputeOrPrimitiveVertexIndexLoad32Bit;
|
||||
}
|
||||
}
|
||||
vertex_shader_index_load_out = vertex_shader_index_load;
|
||||
// W0 division control.
|
||||
// http://www.x.org/docs/AMD/old/evergreen_3D_registers_v2.pdf
|
||||
// 8: VTX_XY_FMT = true: the incoming XY have already been multiplied by 1/W0.
|
||||
@@ -3466,9 +3448,9 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
|
||||
|
||||
// Index or tessellation edge factor buffer endianness.
|
||||
dirty |= system_constants_.vertex_index_endian !=
|
||||
primitive_processing_result.host_index_endian;
|
||||
primitive_processing_result.host_shader_index_endian;
|
||||
system_constants_.vertex_index_endian =
|
||||
primitive_processing_result.host_index_endian;
|
||||
primitive_processing_result.host_shader_index_endian;
|
||||
|
||||
// Vertex index offset.
|
||||
dirty |= system_constants_.vertex_base_index != vgt_indx_offset;
|
||||
@@ -3482,6 +3464,49 @@ void VulkanCommandProcessor::UpdateSystemConstantValues(
|
||||
system_constants_.ndc_offset[i] = viewport_info.ndc_offset[i];
|
||||
}
|
||||
|
||||
// Point size.
|
||||
if (vgt_draw_initiator.prim_type == xenos::PrimitiveType::kPointList) {
|
||||
auto pa_su_point_minmax = regs.Get<reg::PA_SU_POINT_MINMAX>();
|
||||
auto pa_su_point_size = regs.Get<reg::PA_SU_POINT_SIZE>();
|
||||
float point_vertex_diameter_min =
|
||||
float(pa_su_point_minmax.min_size) * (2.0f / 16.0f);
|
||||
float point_vertex_diameter_max =
|
||||
float(pa_su_point_minmax.max_size) * (2.0f / 16.0f);
|
||||
float point_constant_diameter_x =
|
||||
float(pa_su_point_size.width) * (2.0f / 16.0f);
|
||||
float point_constant_diameter_y =
|
||||
float(pa_su_point_size.height) * (2.0f / 16.0f);
|
||||
dirty |= system_constants_.point_vertex_diameter_min !=
|
||||
point_vertex_diameter_min;
|
||||
dirty |= system_constants_.point_vertex_diameter_max !=
|
||||
point_vertex_diameter_max;
|
||||
dirty |= system_constants_.point_constant_diameter[0] !=
|
||||
point_constant_diameter_x;
|
||||
dirty |= system_constants_.point_constant_diameter[1] !=
|
||||
point_constant_diameter_y;
|
||||
system_constants_.point_vertex_diameter_min = point_vertex_diameter_min;
|
||||
system_constants_.point_vertex_diameter_max = point_vertex_diameter_max;
|
||||
system_constants_.point_constant_diameter[0] = point_constant_diameter_x;
|
||||
system_constants_.point_constant_diameter[1] = point_constant_diameter_y;
|
||||
// 2 because 1 in the NDC is half of the viewport's axis, 0.5 for diameter
|
||||
// to radius conversion to avoid multiplying the per-vertex diameter by an
|
||||
// additional constant in the shader.
|
||||
float point_screen_diameter_to_ndc_radius_x =
|
||||
(/* 0.5f * 2.0f * */ float(texture_cache_->draw_resolution_scale_x())) /
|
||||
std::max(viewport_info.xy_extent[0], uint32_t(1));
|
||||
float point_screen_diameter_to_ndc_radius_y =
|
||||
(/* 0.5f * 2.0f * */ float(texture_cache_->draw_resolution_scale_y())) /
|
||||
std::max(viewport_info.xy_extent[1], uint32_t(1));
|
||||
dirty |= system_constants_.point_screen_diameter_to_ndc_radius[0] !=
|
||||
point_screen_diameter_to_ndc_radius_x;
|
||||
dirty |= system_constants_.point_screen_diameter_to_ndc_radius[1] !=
|
||||
point_screen_diameter_to_ndc_radius_y;
|
||||
system_constants_.point_screen_diameter_to_ndc_radius[0] =
|
||||
point_screen_diameter_to_ndc_radius_x;
|
||||
system_constants_.point_screen_diameter_to_ndc_radius[1] =
|
||||
point_screen_diameter_to_ndc_radius_y;
|
||||
}
|
||||
|
||||
// Texture signedness / gamma.
|
||||
{
|
||||
uint32_t textures_remaining = used_texture_mask;
|
||||
|
||||
@@ -436,8 +436,8 @@ class VulkanCommandProcessor : public CommandProcessor {
|
||||
void UpdateSystemConstantValues(
|
||||
bool primitive_polygonal,
|
||||
const PrimitiveProcessor::ProcessingResult& primitive_processing_result,
|
||||
const draw_util::ViewportInfo& viewport_info, uint32_t used_texture_mask,
|
||||
bool& vertex_shader_index_load_out);
|
||||
bool shader_32bit_index_dma, const draw_util::ViewportInfo& viewport_info,
|
||||
uint32_t used_texture_mask);
|
||||
bool UpdateBindings(const VulkanShader* vertex_shader,
|
||||
const VulkanShader* pixel_shader);
|
||||
// Allocates a descriptor set and fills one or two VkWriteDescriptorSet
|
||||
|
||||
@@ -118,7 +118,7 @@ VulkanShader* VulkanPipelineCache::LoadShader(xenos::ShaderType shader_type,
|
||||
SpirvShaderTranslator::Modification
|
||||
VulkanPipelineCache::GetCurrentVertexShaderModification(
|
||||
const Shader& shader, Shader::HostVertexShaderType host_vertex_shader_type,
|
||||
uint32_t interpolator_mask) const {
|
||||
uint32_t interpolator_mask, bool ps_param_gen_used) const {
|
||||
assert_true(shader.type() == xenos::ShaderType::kVertex);
|
||||
assert_true(shader.is_ucode_analyzed());
|
||||
const auto& regs = register_file_;
|
||||
@@ -133,6 +133,16 @@ VulkanPipelineCache::GetCurrentVertexShaderModification(
|
||||
|
||||
modification.vertex.interpolator_mask = interpolator_mask;
|
||||
|
||||
if (host_vertex_shader_type ==
|
||||
Shader::HostVertexShaderType::kPointListAsTriangleStrip) {
|
||||
modification.vertex.output_point_parameters = uint32_t(ps_param_gen_used);
|
||||
} else {
|
||||
modification.vertex.output_point_parameters =
|
||||
uint32_t((shader.writes_point_size_edge_flag_kill_vertex() & 0b001) &&
|
||||
regs.Get<reg::VGT_DRAW_INITIATOR>().prim_type ==
|
||||
xenos::PrimitiveType::kPointList);
|
||||
}
|
||||
|
||||
return modification;
|
||||
}
|
||||
|
||||
@@ -284,6 +294,8 @@ bool VulkanPipelineCache::ConfigurePipeline(
|
||||
if (GetGeometryShaderKey(
|
||||
description.geometry_shader,
|
||||
SpirvShaderTranslator::Modification(vertex_shader->modification()),
|
||||
SpirvShaderTranslator::Modification(
|
||||
pixel_shader ? pixel_shader->modification() : 0),
|
||||
geometry_shader_key)) {
|
||||
geometry_shader = GetGeometryShader(geometry_shader_key);
|
||||
if (geometry_shader == VK_NULL_HANDLE) {
|
||||
@@ -496,6 +508,7 @@ bool VulkanPipelineCache::GetCurrentStateDescription(
|
||||
PipelinePrimitiveTopology primitive_topology;
|
||||
switch (primitive_processing_result.host_primitive_type) {
|
||||
case xenos::PrimitiveType::kPointList:
|
||||
geometry_shader = PipelineGeometryShader::kPointList;
|
||||
primitive_topology = PipelinePrimitiveTopology::kPointList;
|
||||
break;
|
||||
case xenos::PrimitiveType::kLineList:
|
||||
@@ -815,10 +828,22 @@ bool VulkanPipelineCache::ArePipelineRequirementsMet(
|
||||
bool VulkanPipelineCache::GetGeometryShaderKey(
|
||||
PipelineGeometryShader geometry_shader_type,
|
||||
SpirvShaderTranslator::Modification vertex_shader_modification,
|
||||
SpirvShaderTranslator::Modification pixel_shader_modification,
|
||||
GeometryShaderKey& key_out) {
|
||||
if (geometry_shader_type == PipelineGeometryShader::kNone) {
|
||||
return false;
|
||||
}
|
||||
// For kPointListAsTriangleStrip, output_point_parameters has a different
|
||||
// meaning (the coordinates, not the size). However, the AsTriangleStrip host
|
||||
// vertex shader types are needed specifically when geometry shaders are not
|
||||
// supported as fallbacks.
|
||||
if (vertex_shader_modification.vertex.host_vertex_shader_type ==
|
||||
Shader::HostVertexShaderType::kPointListAsTriangleStrip ||
|
||||
vertex_shader_modification.vertex.host_vertex_shader_type ==
|
||||
Shader::HostVertexShaderType::kRectangleListAsTriangleStrip) {
|
||||
assert_always();
|
||||
return false;
|
||||
}
|
||||
GeometryShaderKey key;
|
||||
key.type = geometry_shader_type;
|
||||
// TODO(Triang3l): Once all needed inputs and outputs are added, uncomment the
|
||||
@@ -832,9 +857,8 @@ bool VulkanPipelineCache::GetGeometryShaderKey(
|
||||
key.has_vertex_kill_and =
|
||||
/* vertex_shader_modification.vertex.vertex_kill_and */ 0;
|
||||
key.has_point_size =
|
||||
/* vertex_shader_modification.vertex.output_point_size */ 0;
|
||||
key.has_point_coordinates =
|
||||
/* pixel_shader_modification.pixel.param_gen_point */ 0;
|
||||
vertex_shader_modification.vertex.output_point_parameters;
|
||||
key.has_point_coordinates = pixel_shader_modification.pixel.param_gen_point;
|
||||
key_out = key;
|
||||
return true;
|
||||
}
|
||||
@@ -853,6 +877,13 @@ VkShaderModule VulkanPipelineCache::GetGeometryShader(GeometryShaderKey key) {
|
||||
spv::ExecutionMode output_primitive_execution_mode = spv::ExecutionMode(0);
|
||||
uint32_t output_max_vertices = 0;
|
||||
switch (key.type) {
|
||||
case PipelineGeometryShader::kPointList:
|
||||
// Point to a strip of 2 triangles.
|
||||
input_primitive_execution_mode = spv::ExecutionModeInputPoints;
|
||||
input_primitive_vertex_count = 1;
|
||||
output_primitive_execution_mode = spv::ExecutionModeOutputTriangleStrip;
|
||||
output_max_vertices = 4;
|
||||
break;
|
||||
case PipelineGeometryShader::kRectangleList:
|
||||
// Triangle to a strip of 2 triangles.
|
||||
input_primitive_execution_mode = spv::ExecutionModeTriangles;
|
||||
@@ -901,6 +932,7 @@ VkShaderModule VulkanPipelineCache::GetGeometryShader(GeometryShaderKey key) {
|
||||
spv::Id type_bool4 = builder.makeVectorType(type_bool, 4);
|
||||
spv::Id type_int = builder.makeIntType(32);
|
||||
spv::Id type_float = builder.makeFloatType(32);
|
||||
spv::Id type_float2 = builder.makeVectorType(type_float, 2);
|
||||
spv::Id type_float4 = builder.makeVectorType(type_float, 4);
|
||||
spv::Id type_clip_distances =
|
||||
clip_distance_count
|
||||
@@ -912,9 +944,54 @@ VkShaderModule VulkanPipelineCache::GetGeometryShader(GeometryShaderKey key) {
|
||||
? builder.makeArrayType(
|
||||
type_float, builder.makeUintConstant(cull_distance_count), 0)
|
||||
: spv::NoType;
|
||||
spv::Id type_point_coordinates = key.has_point_coordinates
|
||||
? builder.makeVectorType(type_float, 2)
|
||||
: spv::NoType;
|
||||
|
||||
// System constants.
|
||||
// For points:
|
||||
// - float2 point_constant_diameter
|
||||
// - float2 point_screen_diameter_to_ndc_radius
|
||||
enum PointConstant : uint32_t {
|
||||
kPointConstantConstantDiameter,
|
||||
kPointConstantScreenDiameterToNdcRadius,
|
||||
kPointConstantCount,
|
||||
};
|
||||
spv::Id type_system_constants = spv::NoType;
|
||||
if (key.type == PipelineGeometryShader::kPointList) {
|
||||
id_vector_temp.clear();
|
||||
id_vector_temp.resize(kPointConstantCount);
|
||||
id_vector_temp[kPointConstantConstantDiameter] = type_float2;
|
||||
id_vector_temp[kPointConstantScreenDiameterToNdcRadius] = type_float2;
|
||||
type_system_constants =
|
||||
builder.makeStructType(id_vector_temp, "XeSystemConstants");
|
||||
builder.addMemberName(type_system_constants, kPointConstantConstantDiameter,
|
||||
"point_constant_diameter");
|
||||
builder.addMemberDecoration(
|
||||
type_system_constants, kPointConstantConstantDiameter,
|
||||
spv::DecorationOffset,
|
||||
int(offsetof(SpirvShaderTranslator::SystemConstants,
|
||||
point_constant_diameter)));
|
||||
builder.addMemberName(type_system_constants,
|
||||
kPointConstantScreenDiameterToNdcRadius,
|
||||
"point_screen_diameter_to_ndc_radius");
|
||||
builder.addMemberDecoration(
|
||||
type_system_constants, kPointConstantScreenDiameterToNdcRadius,
|
||||
spv::DecorationOffset,
|
||||
int(offsetof(SpirvShaderTranslator::SystemConstants,
|
||||
point_screen_diameter_to_ndc_radius)));
|
||||
}
|
||||
spv::Id uniform_system_constants = spv::NoResult;
|
||||
if (type_system_constants != spv::NoType) {
|
||||
builder.addDecoration(type_system_constants, spv::DecorationBlock);
|
||||
uniform_system_constants = builder.createVariable(
|
||||
spv::NoPrecision, spv::StorageClassUniform, type_system_constants,
|
||||
"xe_uniform_system_constants");
|
||||
builder.addDecoration(uniform_system_constants,
|
||||
spv::DecorationDescriptorSet,
|
||||
int(SpirvShaderTranslator::kDescriptorSetConstants));
|
||||
builder.addDecoration(uniform_system_constants, spv::DecorationBinding,
|
||||
int(SpirvShaderTranslator::kConstantBufferSystem));
|
||||
// Generating SPIR-V 1.0, no need to add bindings to the entry point's
|
||||
// interface until SPIR-V 1.4.
|
||||
}
|
||||
|
||||
// Inputs and outputs - matching glslang order, in gl_PerVertex gl_in[],
|
||||
// user-defined outputs, user-defined inputs, out gl_PerVertex.
|
||||
@@ -977,6 +1054,8 @@ VkShaderModule VulkanPipelineCache::GetGeometryShader(GeometryShaderKey key) {
|
||||
type_array_in_gl_per_vertex, "gl_in");
|
||||
main_interface.push_back(in_gl_per_vertex);
|
||||
|
||||
uint32_t output_location = 0;
|
||||
|
||||
// Interpolators outputs.
|
||||
std::array<spv::Id, xenos::kMaxInterpolators> out_interpolators;
|
||||
for (uint32_t i = 0; i < key.interpolator_count; ++i) {
|
||||
@@ -984,23 +1063,28 @@ VkShaderModule VulkanPipelineCache::GetGeometryShader(GeometryShaderKey key) {
|
||||
spv::NoPrecision, spv::StorageClassOutput, type_float4,
|
||||
fmt::format("xe_out_interpolator_{}", i).c_str());
|
||||
out_interpolators[i] = out_interpolator;
|
||||
builder.addDecoration(out_interpolator, spv::DecorationLocation, i);
|
||||
builder.addDecoration(out_interpolator, spv::DecorationLocation,
|
||||
int(output_location));
|
||||
builder.addDecoration(out_interpolator, spv::DecorationInvariant);
|
||||
main_interface.push_back(out_interpolator);
|
||||
++output_location;
|
||||
}
|
||||
|
||||
// Point coordinate output.
|
||||
spv::Id out_point_coordinates = spv::NoResult;
|
||||
if (key.has_point_coordinates) {
|
||||
out_point_coordinates = builder.createVariable(
|
||||
spv::NoPrecision, spv::StorageClassOutput, type_point_coordinates,
|
||||
"xe_out_point_coordinates");
|
||||
out_point_coordinates =
|
||||
builder.createVariable(spv::NoPrecision, spv::StorageClassOutput,
|
||||
type_float2, "xe_out_point_coordinates");
|
||||
builder.addDecoration(out_point_coordinates, spv::DecorationLocation,
|
||||
key.interpolator_count);
|
||||
int(output_location));
|
||||
builder.addDecoration(out_point_coordinates, spv::DecorationInvariant);
|
||||
main_interface.push_back(out_point_coordinates);
|
||||
++output_location;
|
||||
}
|
||||
|
||||
uint32_t input_location = 0;
|
||||
|
||||
// Interpolator inputs.
|
||||
std::array<spv::Id, xenos::kMaxInterpolators> in_interpolators;
|
||||
for (uint32_t i = 0; i < key.interpolator_count; ++i) {
|
||||
@@ -1010,8 +1094,10 @@ VkShaderModule VulkanPipelineCache::GetGeometryShader(GeometryShaderKey key) {
|
||||
0),
|
||||
fmt::format("xe_in_interpolator_{}", i).c_str());
|
||||
in_interpolators[i] = in_interpolator;
|
||||
builder.addDecoration(in_interpolator, spv::DecorationLocation, i);
|
||||
builder.addDecoration(in_interpolator, spv::DecorationLocation,
|
||||
int(input_location));
|
||||
main_interface.push_back(in_interpolator);
|
||||
++input_location;
|
||||
}
|
||||
|
||||
// Point size input.
|
||||
@@ -1023,8 +1109,9 @@ VkShaderModule VulkanPipelineCache::GetGeometryShader(GeometryShaderKey key) {
|
||||
0),
|
||||
"xe_in_point_size");
|
||||
builder.addDecoration(in_point_size, spv::DecorationLocation,
|
||||
key.interpolator_count);
|
||||
int(input_location));
|
||||
main_interface.push_back(in_point_size);
|
||||
++input_location;
|
||||
}
|
||||
|
||||
// out gl_PerVertex.
|
||||
@@ -1198,6 +1285,231 @@ VkShaderModule VulkanPipelineCache::GetGeometryShader(GeometryShaderKey key) {
|
||||
}
|
||||
|
||||
switch (key.type) {
|
||||
case PipelineGeometryShader::kPointList: {
|
||||
// Expand the point sprite, with left-to-right, top-to-bottom UVs.
|
||||
|
||||
spv::Id const_int_0 = builder.makeIntConstant(0);
|
||||
spv::Id const_int_1 = builder.makeIntConstant(1);
|
||||
spv::Id const_float_0 = builder.makeFloatConstant(0.0f);
|
||||
|
||||
// Load the point diameter in guest pixels.
|
||||
id_vector_temp.clear();
|
||||
id_vector_temp.reserve(2);
|
||||
id_vector_temp.push_back(
|
||||
builder.makeIntConstant(int32_t(kPointConstantConstantDiameter)));
|
||||
id_vector_temp.push_back(const_int_0);
|
||||
spv::Id point_guest_diameter_x = builder.createLoad(
|
||||
builder.createAccessChain(spv::StorageClassUniform,
|
||||
uniform_system_constants, id_vector_temp),
|
||||
spv::NoPrecision);
|
||||
id_vector_temp.back() = const_int_1;
|
||||
spv::Id point_guest_diameter_y = builder.createLoad(
|
||||
builder.createAccessChain(spv::StorageClassUniform,
|
||||
uniform_system_constants, id_vector_temp),
|
||||
spv::NoPrecision);
|
||||
if (key.has_point_size) {
|
||||
// The vertex shader's header writes -1.0 to point_size by default, so
|
||||
// any non-negative value means that it was overwritten by the
|
||||
// translated vertex shader, and needs to be used instead of the
|
||||
// constant size. The per-vertex diameter is already clamped in the
|
||||
// vertex shader (combined with making it non-negative).
|
||||
id_vector_temp.clear();
|
||||
// 0 is the input primitive vertex index.
|
||||
id_vector_temp.push_back(const_int_0);
|
||||
spv::Id point_vertex_diameter = builder.createLoad(
|
||||
builder.createAccessChain(spv::StorageClassInput, in_point_size,
|
||||
id_vector_temp),
|
||||
spv::NoPrecision);
|
||||
spv::Id point_vertex_diameter_written =
|
||||
builder.createBinOp(spv::OpFOrdGreaterThanEqual, type_bool,
|
||||
point_vertex_diameter, const_float_0);
|
||||
point_guest_diameter_x = builder.createTriOp(
|
||||
spv::OpSelect, type_float, point_vertex_diameter_written,
|
||||
point_vertex_diameter, point_guest_diameter_x);
|
||||
point_guest_diameter_y = builder.createTriOp(
|
||||
spv::OpSelect, type_float, point_vertex_diameter_written,
|
||||
point_vertex_diameter, point_guest_diameter_y);
|
||||
}
|
||||
|
||||
// 4D5307F1 has zero-size snowflakes, drop them quicker, and also drop
|
||||
// points with a constant size of zero since point lists may also be used
|
||||
// as just "compute" with memexport.
|
||||
spv::Id point_size_not_zero = builder.createBinOp(
|
||||
spv::OpLogicalAnd, type_bool,
|
||||
builder.createBinOp(spv::OpFOrdGreaterThan, type_bool,
|
||||
point_guest_diameter_x, const_float_0),
|
||||
builder.createBinOp(spv::OpFOrdGreaterThan, type_bool,
|
||||
point_guest_diameter_y, const_float_0));
|
||||
spv::Block& point_size_zero_predecessor = *builder.getBuildPoint();
|
||||
spv::Block& point_size_zero_then_block = builder.makeNewBlock();
|
||||
spv::Block& point_size_zero_merge_block = builder.makeNewBlock();
|
||||
{
|
||||
std::unique_ptr<spv::Instruction> selection_merge_op(
|
||||
std::make_unique<spv::Instruction>(spv::OpSelectionMerge));
|
||||
selection_merge_op->addIdOperand(point_size_zero_merge_block.getId());
|
||||
selection_merge_op->addImmediateOperand(
|
||||
spv::SelectionControlDontFlattenMask);
|
||||
point_size_zero_predecessor.addInstruction(
|
||||
std::move(selection_merge_op));
|
||||
}
|
||||
{
|
||||
std::unique_ptr<spv::Instruction> branch_conditional_op(
|
||||
std::make_unique<spv::Instruction>(spv::OpBranchConditional));
|
||||
branch_conditional_op->addIdOperand(point_size_not_zero);
|
||||
branch_conditional_op->addIdOperand(
|
||||
point_size_zero_merge_block.getId());
|
||||
branch_conditional_op->addIdOperand(point_size_zero_then_block.getId());
|
||||
branch_conditional_op->addImmediateOperand(2);
|
||||
branch_conditional_op->addImmediateOperand(1);
|
||||
point_size_zero_predecessor.addInstruction(
|
||||
std::move(branch_conditional_op));
|
||||
}
|
||||
point_size_zero_then_block.addPredecessor(&point_size_zero_predecessor);
|
||||
point_size_zero_merge_block.addPredecessor(&point_size_zero_predecessor);
|
||||
builder.setBuildPoint(&point_size_zero_then_block);
|
||||
builder.createNoResultOp(spv::OpReturn);
|
||||
builder.setBuildPoint(&point_size_zero_merge_block);
|
||||
|
||||
// Transform the diameter in the guest screen coordinates to radius in the
|
||||
// normalized device coordinates, and then to the clip space by
|
||||
// multiplying by W.
|
||||
id_vector_temp.clear();
|
||||
id_vector_temp.reserve(2);
|
||||
id_vector_temp.push_back(builder.makeIntConstant(
|
||||
int32_t(kPointConstantScreenDiameterToNdcRadius)));
|
||||
id_vector_temp.push_back(const_int_0);
|
||||
spv::Id point_radius_x = builder.createBinOp(
|
||||
spv::OpFMul, type_float, point_guest_diameter_x,
|
||||
builder.createLoad(builder.createAccessChain(spv::StorageClassUniform,
|
||||
uniform_system_constants,
|
||||
id_vector_temp),
|
||||
spv::NoPrecision));
|
||||
builder.addDecoration(point_radius_x, spv::DecorationNoContraction);
|
||||
id_vector_temp.back() = const_int_1;
|
||||
spv::Id point_radius_y = builder.createBinOp(
|
||||
spv::OpFMul, type_float, point_guest_diameter_y,
|
||||
builder.createLoad(builder.createAccessChain(spv::StorageClassUniform,
|
||||
uniform_system_constants,
|
||||
id_vector_temp),
|
||||
spv::NoPrecision));
|
||||
builder.addDecoration(point_radius_y, spv::DecorationNoContraction);
|
||||
id_vector_temp.clear();
|
||||
id_vector_temp.reserve(2);
|
||||
// 0 is the input primitive vertex index.
|
||||
id_vector_temp.push_back(const_int_0);
|
||||
id_vector_temp.push_back(const_member_in_gl_per_vertex_position);
|
||||
spv::Id point_position = builder.createLoad(
|
||||
builder.createAccessChain(spv::StorageClassInput, in_gl_per_vertex,
|
||||
id_vector_temp),
|
||||
spv::NoPrecision);
|
||||
spv::Id point_w =
|
||||
builder.createCompositeExtract(point_position, type_float, 3);
|
||||
point_radius_x =
|
||||
builder.createBinOp(spv::OpFMul, type_float, point_radius_x, point_w);
|
||||
builder.addDecoration(point_radius_x, spv::DecorationNoContraction);
|
||||
point_radius_y =
|
||||
builder.createBinOp(spv::OpFMul, type_float, point_radius_y, point_w);
|
||||
builder.addDecoration(point_radius_y, spv::DecorationNoContraction);
|
||||
|
||||
// Load the inputs for the guest point.
|
||||
// Interpolators.
|
||||
std::array<spv::Id, xenos::kMaxInterpolators> point_interpolators;
|
||||
id_vector_temp.clear();
|
||||
// 0 is the input primitive vertex index.
|
||||
id_vector_temp.push_back(const_int_0);
|
||||
for (uint32_t i = 0; i < key.interpolator_count; ++i) {
|
||||
point_interpolators[i] = builder.createLoad(
|
||||
builder.createAccessChain(spv::StorageClassInput,
|
||||
in_interpolators[i], id_vector_temp),
|
||||
spv::NoPrecision);
|
||||
}
|
||||
// Positions.
|
||||
spv::Id point_x =
|
||||
builder.createCompositeExtract(point_position, type_float, 0);
|
||||
spv::Id point_y =
|
||||
builder.createCompositeExtract(point_position, type_float, 1);
|
||||
std::array<spv::Id, 2> point_edge_x, point_edge_y;
|
||||
for (uint32_t i = 0; i < 2; ++i) {
|
||||
spv::Op point_radius_add_op = i ? spv::OpFAdd : spv::OpFSub;
|
||||
point_edge_x[i] = builder.createBinOp(point_radius_add_op, type_float,
|
||||
point_x, point_radius_x);
|
||||
builder.addDecoration(point_edge_x[i], spv::DecorationNoContraction);
|
||||
point_edge_y[i] = builder.createBinOp(point_radius_add_op, type_float,
|
||||
point_y, point_radius_y);
|
||||
builder.addDecoration(point_edge_y[i], spv::DecorationNoContraction);
|
||||
};
|
||||
spv::Id point_z =
|
||||
builder.createCompositeExtract(point_position, type_float, 2);
|
||||
// Clip distances.
|
||||
spv::Id point_clip_distances = spv::NoResult;
|
||||
if (clip_distance_count) {
|
||||
id_vector_temp.clear();
|
||||
id_vector_temp.reserve(2);
|
||||
// 0 is the input primitive vertex index.
|
||||
id_vector_temp.push_back(const_int_0);
|
||||
id_vector_temp.push_back(const_member_in_gl_per_vertex_clip_distance);
|
||||
point_clip_distances = builder.createLoad(
|
||||
builder.createAccessChain(spv::StorageClassInput, in_gl_per_vertex,
|
||||
id_vector_temp),
|
||||
spv::NoPrecision);
|
||||
}
|
||||
|
||||
for (uint32_t i = 0; i < 4; ++i) {
|
||||
// Same interpolators for the entire sprite.
|
||||
for (uint32_t j = 0; j < key.interpolator_count; ++j) {
|
||||
builder.createStore(point_interpolators[j], out_interpolators[j]);
|
||||
}
|
||||
// Top-left, bottom-left, top-right, bottom-right order (chosen
|
||||
// arbitrarily, simply based on counterclockwise meaning front with
|
||||
// frontFace = VkFrontFace(0), but faceness is ignored for non-polygon
|
||||
// primitive types).
|
||||
uint32_t point_vertex_x = i >> 1;
|
||||
uint32_t point_vertex_y = i & 1;
|
||||
// Point coordinates.
|
||||
if (key.has_point_coordinates) {
|
||||
id_vector_temp.clear();
|
||||
id_vector_temp.reserve(2);
|
||||
id_vector_temp.push_back(
|
||||
builder.makeFloatConstant(float(point_vertex_x)));
|
||||
id_vector_temp.push_back(
|
||||
builder.makeFloatConstant(float(point_vertex_y)));
|
||||
builder.createStore(
|
||||
builder.makeCompositeConstant(type_float2, id_vector_temp),
|
||||
out_point_coordinates);
|
||||
}
|
||||
// Position.
|
||||
id_vector_temp.clear();
|
||||
id_vector_temp.reserve(4);
|
||||
id_vector_temp.push_back(point_edge_x[point_vertex_x]);
|
||||
id_vector_temp.push_back(point_edge_y[point_vertex_y]);
|
||||
id_vector_temp.push_back(point_z);
|
||||
id_vector_temp.push_back(point_w);
|
||||
spv::Id point_vertex_position =
|
||||
builder.createCompositeConstruct(type_float4, id_vector_temp);
|
||||
id_vector_temp.clear();
|
||||
id_vector_temp.push_back(const_member_out_gl_per_vertex_position);
|
||||
builder.createStore(
|
||||
point_vertex_position,
|
||||
builder.createAccessChain(spv::StorageClassOutput,
|
||||
out_gl_per_vertex, id_vector_temp));
|
||||
// Clip distances.
|
||||
// TODO(Triang3l): Handle ps_ucp_mode properly, clip expanded points if
|
||||
// needed.
|
||||
if (clip_distance_count) {
|
||||
id_vector_temp.clear();
|
||||
id_vector_temp.push_back(
|
||||
const_member_out_gl_per_vertex_clip_distance);
|
||||
builder.createStore(
|
||||
point_clip_distances,
|
||||
builder.createAccessChain(spv::StorageClassOutput,
|
||||
out_gl_per_vertex, id_vector_temp));
|
||||
}
|
||||
// Emit the vertex.
|
||||
builder.createNoResultOp(spv::OpEmitVertex);
|
||||
}
|
||||
builder.createNoResultOp(spv::OpEndPrimitive);
|
||||
} break;
|
||||
|
||||
case PipelineGeometryShader::kRectangleList: {
|
||||
// Construct a strip with the fourth vertex generated by mirroring a
|
||||
// vertex across the longest edge (the diagonal).
|
||||
@@ -1308,8 +1620,8 @@ VkShaderModule VulkanPipelineCache::GetGeometryShader(GeometryShaderKey key) {
|
||||
id_vector_temp.reserve(2);
|
||||
id_vector_temp.push_back(const_float_0);
|
||||
id_vector_temp.push_back(const_float_0);
|
||||
const_point_coordinates_zero = builder.makeCompositeConstant(
|
||||
type_point_coordinates, id_vector_temp);
|
||||
const_point_coordinates_zero =
|
||||
builder.makeCompositeConstant(type_float2, id_vector_temp);
|
||||
}
|
||||
|
||||
// Emit the triangle in the strip that consists of the original vertices.
|
||||
@@ -1491,8 +1803,8 @@ VkShaderModule VulkanPipelineCache::GetGeometryShader(GeometryShaderKey key) {
|
||||
id_vector_temp.reserve(2);
|
||||
id_vector_temp.push_back(const_float_0);
|
||||
id_vector_temp.push_back(const_float_0);
|
||||
const_point_coordinates_zero = builder.makeCompositeConstant(
|
||||
type_point_coordinates, id_vector_temp);
|
||||
const_point_coordinates_zero =
|
||||
builder.makeCompositeConstant(type_float2, id_vector_temp);
|
||||
}
|
||||
|
||||
// Build the triangle strip from the original quad vertices in the
|
||||
|
||||
@@ -71,7 +71,7 @@ class VulkanPipelineCache {
|
||||
SpirvShaderTranslator::Modification GetCurrentVertexShaderModification(
|
||||
const Shader& shader,
|
||||
Shader::HostVertexShaderType host_vertex_shader_type,
|
||||
uint32_t interpolator_mask) const;
|
||||
uint32_t interpolator_mask, bool ps_param_gen_used) const;
|
||||
SpirvShaderTranslator::Modification GetCurrentPixelShaderModification(
|
||||
const Shader& shader, uint32_t interpolator_mask,
|
||||
uint32_t param_gen_pos) const;
|
||||
@@ -92,6 +92,7 @@ class VulkanPipelineCache {
|
||||
private:
|
||||
enum class PipelineGeometryShader : uint32_t {
|
||||
kNone,
|
||||
kPointList,
|
||||
kRectangleList,
|
||||
kQuadList,
|
||||
};
|
||||
@@ -267,6 +268,7 @@ class VulkanPipelineCache {
|
||||
static bool GetGeometryShaderKey(
|
||||
PipelineGeometryShader geometry_shader_type,
|
||||
SpirvShaderTranslator::Modification vertex_shader_modification,
|
||||
SpirvShaderTranslator::Modification pixel_shader_modification,
|
||||
GeometryShaderKey& key_out);
|
||||
VkShaderModule GetGeometryShader(GeometryShaderKey key);
|
||||
|
||||
|
||||
@@ -27,17 +27,18 @@ namespace vulkan {
|
||||
VulkanPrimitiveProcessor::~VulkanPrimitiveProcessor() { Shutdown(true); }
|
||||
|
||||
bool VulkanPrimitiveProcessor::Initialize() {
|
||||
// TODO(Triang3l): fullDrawIndexUint32 feature check and indirect index fetch.
|
||||
const ui::vulkan::VulkanProvider& provider =
|
||||
command_processor_.GetVulkanProvider();
|
||||
const VkPhysicalDeviceFeatures& device_features = provider.device_features();
|
||||
const VkPhysicalDevicePortabilitySubsetFeaturesKHR*
|
||||
device_portability_subset_features =
|
||||
provider.device_portability_subset_features();
|
||||
if (!InitializeCommon(true,
|
||||
if (!InitializeCommon(device_features.fullDrawIndexUint32,
|
||||
!device_portability_subset_features ||
|
||||
device_portability_subset_features->triangleFans,
|
||||
false, device_features.geometryShader)) {
|
||||
false, device_features.geometryShader,
|
||||
device_features.geometryShader,
|
||||
device_features.geometryShader)) {
|
||||
Shutdown();
|
||||
return false;
|
||||
}
|
||||
@@ -128,9 +129,9 @@ void VulkanPrimitiveProcessor::EndFrame() {
|
||||
frame_index_buffers_.clear();
|
||||
}
|
||||
|
||||
bool VulkanPrimitiveProcessor::InitializeBuiltin16BitIndexBuffer(
|
||||
uint32_t index_count, std::function<void(uint16_t*)> fill_callback) {
|
||||
assert_not_zero(index_count);
|
||||
bool VulkanPrimitiveProcessor::InitializeBuiltinIndexBuffer(
|
||||
size_t size_bytes, std::function<void(void*)> fill_callback) {
|
||||
assert_not_zero(size_bytes);
|
||||
assert_true(builtin_index_buffer_ == VK_NULL_HANDLE);
|
||||
assert_true(builtin_index_buffer_memory_ == VK_NULL_HANDLE);
|
||||
assert_true(builtin_index_buffer_upload_ == VK_NULL_HANDLE);
|
||||
@@ -141,7 +142,7 @@ bool VulkanPrimitiveProcessor::InitializeBuiltin16BitIndexBuffer(
|
||||
const ui::vulkan::VulkanProvider::DeviceFunctions& dfn = provider.dfn();
|
||||
VkDevice device = provider.device();
|
||||
|
||||
builtin_index_buffer_size_ = VkDeviceSize(sizeof(uint16_t) * index_count);
|
||||
builtin_index_buffer_size_ = VkDeviceSize(size_bytes);
|
||||
if (!ui::vulkan::util::CreateDedicatedAllocationBuffer(
|
||||
provider, builtin_index_buffer_size_,
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
|
||||
@@ -149,8 +150,8 @@ bool VulkanPrimitiveProcessor::InitializeBuiltin16BitIndexBuffer(
|
||||
builtin_index_buffer_memory_)) {
|
||||
XELOGE(
|
||||
"Vulkan primitive processor: Failed to create the built-in index "
|
||||
"buffer GPU resource with {} 16-bit indices",
|
||||
index_count);
|
||||
"buffer GPU resource with {} bytes",
|
||||
size_bytes);
|
||||
return false;
|
||||
}
|
||||
uint32_t upload_memory_type;
|
||||
@@ -162,8 +163,8 @@ bool VulkanPrimitiveProcessor::InitializeBuiltin16BitIndexBuffer(
|
||||
&upload_memory_type)) {
|
||||
XELOGE(
|
||||
"Vulkan primitive processor: Failed to create the built-in index "
|
||||
"buffer upload resource with {} 16-bit indices",
|
||||
index_count);
|
||||
"buffer upload resource with {} bytes",
|
||||
size_bytes);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyBuffer, device,
|
||||
builtin_index_buffer_);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkFreeMemory, device,
|
||||
@@ -176,8 +177,8 @@ bool VulkanPrimitiveProcessor::InitializeBuiltin16BitIndexBuffer(
|
||||
VK_WHOLE_SIZE, 0, &mapping) != VK_SUCCESS) {
|
||||
XELOGE(
|
||||
"Vulkan primitive processor: Failed to map the built-in index buffer "
|
||||
"upload resource with {} 16-bit indices",
|
||||
index_count);
|
||||
"upload resource with {} bytes",
|
||||
size_bytes);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkDestroyBuffer, device,
|
||||
builtin_index_buffer_upload_);
|
||||
ui::vulkan::util::DestroyAndNullHandle(dfn.vkFreeMemory, device,
|
||||
@@ -188,7 +189,7 @@ bool VulkanPrimitiveProcessor::InitializeBuiltin16BitIndexBuffer(
|
||||
builtin_index_buffer_memory_);
|
||||
return false;
|
||||
}
|
||||
fill_callback(reinterpret_cast<uint16_t*>(mapping));
|
||||
fill_callback(mapping);
|
||||
ui::vulkan::util::FlushMappedMemoryRange(
|
||||
provider, builtin_index_buffer_memory_, upload_memory_type);
|
||||
dfn.vkUnmapMemory(device, builtin_index_buffer_upload_memory_);
|
||||
|
||||
@@ -56,9 +56,8 @@ class VulkanPrimitiveProcessor final : public PrimitiveProcessor {
|
||||
}
|
||||
|
||||
protected:
|
||||
bool InitializeBuiltin16BitIndexBuffer(
|
||||
uint32_t index_count,
|
||||
std::function<void(uint16_t*)> fill_callback) override;
|
||||
bool InitializeBuiltinIndexBuffer(
|
||||
size_t size_bytes, std::function<void(void*)> fill_callback) override;
|
||||
|
||||
void* RequestHostConvertedIndexBufferForCurrentFrame(
|
||||
xenos::IndexFormat format, uint32_t index_count, bool coalign_for_simd,
|
||||
|
||||
Reference in New Issue
Block a user