Compare commits
16 Commits
1016_part3
...
astro_play
| Author | SHA1 | Date |
|---|---|---|
|
|
248aed8e96 | |
|
|
e1b2928b6a | |
|
|
cbee451aed | |
|
|
c9119da5a8 | |
|
|
ebfc9b0887 | |
|
|
cccee2d368 | |
|
|
5165b8760c | |
|
|
d7cbb1ca50 | |
|
|
c4b381c9d2 | |
|
|
b60eceab87 | |
|
|
b56d0da86d | |
|
|
34a2801a04 | |
|
|
771459afc7 | |
|
|
49c6b055c4 | |
|
|
dddee59bd9 | |
|
|
6e2933e49c |
|
|
@ -725,15 +725,20 @@ public sealed unsafe partial class GuestImageCache
|
|||
}
|
||||
|
||||
var range = image.Description.Data;
|
||||
var download = _bufferCache.GetUtilityBuffer(GpuBufferUsage.Download);
|
||||
if (!download.TryMap(range.Size, out var offset, Math.Max(image.Description.BytesPerBlock, 4u)))
|
||||
var ring = _bufferCache.GetUtilityBuffer(GpuBufferUsage.Download);
|
||||
GpuBuffer download = ring;
|
||||
if (ring.TryMap(range.Size, out var offset, Math.Max(image.Description.BytesPerBlock, 4u)))
|
||||
{
|
||||
throw SubmissionScheduler.Fatal($"The reusable download ring cannot map the image: address=0x{range.Address:X16} size=0x{range.Size:X}.");
|
||||
ring.Commit();
|
||||
}
|
||||
else
|
||||
{
|
||||
download = new GpuBuffer(_device, _scheduler, GpuBufferUsage.Download, 0, GpuBuffer.AllFlags, range.Size);
|
||||
}
|
||||
|
||||
download.Commit();
|
||||
if (!_backing.TryReadBacking(range.Address, download.Mapped.Slice((int)offset, (int)range.Size)))
|
||||
{
|
||||
if (download != ring) download.Dispose();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -761,6 +766,8 @@ public sealed unsafe partial class GuestImageCache
|
|||
throw SubmissionScheduler.Fatal($"The image readback could not be written to guest memory: address=0x{range.Address:X16} size=0x{range.Size:X}.");
|
||||
}
|
||||
});
|
||||
// Completion actions wait for the priority readback before freeing spill buffers.
|
||||
if (download != ring) _scheduler.QueueCompletionAction(download.Dispose);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -300,6 +300,14 @@ public static partial class ImageRequestBuilders
|
|||
description.BytesPerBlock = blockBytes != 0 ? blockBytes : GuestPixelFormats.BytesPerElement(format);
|
||||
description.Samples = samples;
|
||||
description.TileMode = tile;
|
||||
var metadataAddress = descriptor.MetadataAddress << 8;
|
||||
if (!storage && type == GuestImageType.Color2D && samples == 1 && levels == 1 && tile == GuestTileMode.RenderTarget && descriptor.MetadataCompress &&
|
||||
metadataAddress != 0 && metadataAddress < TrackerLayout.SpaceBytes)
|
||||
{
|
||||
description.Metadata.Kind = MetadataKind.Dcc;
|
||||
description.Metadata.Range = new GuestSpan(metadataAddress, 0);
|
||||
}
|
||||
|
||||
if (samples > 1)
|
||||
{
|
||||
description.MipLayout[0] = new MipLevelLayout { Offset = 0, Size = size.Size, Pitch = pitch, Height = height };
|
||||
|
|
|
|||
|
|
@ -44,6 +44,7 @@ internal interface IShaderPipelineHost
|
|||
bool ExecGuardElisionEnabled => true;
|
||||
ShaderPrewarmList? ShaderPrewarm => null;
|
||||
bool PerVertexPixelInputsSupported => true;
|
||||
bool ClipDistanceEnabled => false;
|
||||
|
||||
RenderHostLimits Limits { get; }
|
||||
|
||||
|
|
|
|||
|
|
@ -608,6 +608,7 @@ internal sealed class ShaderProgramCache
|
|||
RequiredVertexOutputCount = options.RequiredVertexOutputCount,
|
||||
VertexInputs = entry.VertexInputs,
|
||||
PositionExportControl = info.PositionExportControl,
|
||||
SupportsClipDistance = _host.ClipDistanceEnabled,
|
||||
ClipSpace = new ShaderClipSpaceTransform(
|
||||
info.ClipSpace.Enabled,
|
||||
info.ClipSpace.ScaleX,
|
||||
|
|
|
|||
|
|
@ -71,6 +71,7 @@ public static class VertexAttributeFormats
|
|||
56 => (Format.R8G8B8A8Unorm, 4u),
|
||||
50 => (Format.A2B10G10R10UnormPack32, 4u),
|
||||
51 => (Format.A2B10G10R10SNormPack32, 4u),
|
||||
36 => (Format.B10G11R11UfloatPack32, 3u),
|
||||
29 => (Format.R16G16Sfloat, 2u),
|
||||
28 => (Format.R16G16Sint, 2u),
|
||||
27 => (Format.R16G16Uint, 2u),
|
||||
|
|
|
|||
|
|
@ -742,6 +742,7 @@ internal static unsafe partial class VulkanVideoPresenter
|
|||
_supportsIndependentBlend = supportedFeatures.IndependentBlend;
|
||||
_supportsDepthBiasClamp = supportedFeatures.DepthBiasClamp;
|
||||
_supportsDepthBounds = supportedFeatures.DepthBounds;
|
||||
_supportsShaderClipDistance = supportedFeatures.ShaderClipDistance;
|
||||
_supportsFillRectangle = IsDeviceExtensionAvailable(FillRectangleExtensionName);
|
||||
var enabledFeatures = new PhysicalDeviceFeatures
|
||||
{
|
||||
|
|
@ -751,6 +752,8 @@ internal static unsafe partial class VulkanVideoPresenter
|
|||
FragmentStoresAndAtomics = supportedFeatures.FragmentStoresAndAtomics,
|
||||
ShaderInt64 = supportedFeatures.ShaderInt64,
|
||||
ShaderFloat64 = supportedFeatures.ShaderFloat64,
|
||||
ShaderClipDistance = supportedFeatures.ShaderClipDistance,
|
||||
ShaderCullDistance = supportedFeatures.ShaderCullDistance,
|
||||
ShaderImageGatherExtended = supportedFeatures.ShaderImageGatherExtended,
|
||||
ShaderStorageImageExtendedFormats = supportedFeatures.ShaderStorageImageExtendedFormats,
|
||||
ShaderStorageImageReadWithoutFormat = supportedFeatures.ShaderStorageImageReadWithoutFormat,
|
||||
|
|
|
|||
|
|
@ -90,6 +90,7 @@ internal static unsafe partial class VulkanVideoPresenter
|
|||
// NVIDIA's compiler rejects the elided-EXEC wave64 compute module with NVVM error 3.
|
||||
bool IShaderPipelineHost.ExecGuardElisionEnabled => _physicalDeviceVendorId != NvidiaVendorId;
|
||||
bool IShaderPipelineHost.PerVertexPixelInputsSupported => _supportsPerVertexPixelInputs;
|
||||
bool IShaderPipelineHost.ClipDistanceEnabled => _supportsShaderClipDistance;
|
||||
|
||||
RenderHostLimits IShaderPipelineHost.Limits => _renderHostLimits;
|
||||
|
||||
|
|
|
|||
|
|
@ -126,6 +126,7 @@ internal static unsafe partial class VulkanVideoPresenter
|
|||
private bool _supportsDepthClipControl;
|
||||
private bool _supportsDepthClipEnable;
|
||||
private bool _supportsDepthBounds;
|
||||
private bool _supportsShaderClipDistance;
|
||||
private bool _supportsFillRectangle;
|
||||
private RenderHostLimits _renderHostLimits;
|
||||
private IGuestBackedSpace _guestBacking = null!;
|
||||
|
|
@ -602,7 +603,7 @@ internal static unsafe partial class VulkanVideoPresenter
|
|||
_vk.CmdClearColorImage(command, image.Backing.Handle, ImageLayout.TransferDstOptimal, &clearValue, 1, &vkRange);
|
||||
if (!tracked)
|
||||
{
|
||||
_bufferCache.FillBuffer(guestSlice, sliceSize, uint.MaxValue, false);
|
||||
_bufferCache.FillDccMetadata(guestSlice, sliceSize, uint.MaxValue);
|
||||
if (RenderTrace.Enabled && RenderTrace.MetadataClear())
|
||||
{
|
||||
RenderTrace.Write(
|
||||
|
|
|
|||
|
|
@ -185,6 +185,7 @@ public static partial class Gen5SpirvTranslator
|
|||
private uint _layerOutput;
|
||||
private uint _viewportIndexOutput;
|
||||
private uint _clipDistanceCount;
|
||||
private uint _invalidPositionClipDistance = uint.MaxValue;
|
||||
private uint _cullDistanceCount;
|
||||
private uint _vertexIndexInput;
|
||||
private uint _instanceIndexInput;
|
||||
|
|
@ -6588,6 +6589,20 @@ public static partial class Gen5SpirvTranslator
|
|||
outputValue,
|
||||
Load(_vec4Type, outputVariable));
|
||||
Store(outputVariable, outputValue);
|
||||
if (export.Target == 12 && _invalidPositionClipDistance != uint.MaxValue)
|
||||
{
|
||||
var equal = _module.AddInstruction(
|
||||
SpirvOp.FOrdEqual,
|
||||
_module.TypeVector(_boolType, 4),
|
||||
outputValue,
|
||||
_module.ConstantNull(_vec4Type));
|
||||
var invalid = _module.AddInstruction(SpirvOp.All, _boolType, equal);
|
||||
// A zero position has an undefined perspective divide. Collapse its primitive
|
||||
// to the remaining edge, as the guest's clipping-error cull does.
|
||||
var distance = _module.AddInstruction(
|
||||
SpirvOp.Select, _floatType, invalid, Float(-1f), Float(0f));
|
||||
StoreDistanceConditional(_clipDistanceOutput, _invalidPositionClipDistance, distance);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -6696,6 +6711,14 @@ public static partial class Gen5SpirvTranslator
|
|||
}
|
||||
}
|
||||
|
||||
if (_request.SupportsClipDistance && clipCount + cullCount < 8 &&
|
||||
_request.Program.Instructions.Any(static instruction =>
|
||||
instruction.Control is Gen5ExportControl { Target: 12, EnableMask: not 0 }))
|
||||
{
|
||||
// Use a separate plane so auxiliary position exports keep their own distances.
|
||||
_invalidPositionClipDistance = clipCount++;
|
||||
}
|
||||
|
||||
if (needPointSize)
|
||||
{
|
||||
_pointSizeOutput = DeclareBuiltInOutput(
|
||||
|
|
|
|||
|
|
@ -186,6 +186,7 @@ public sealed class ShaderCompileRequest
|
|||
public int RequiredVertexOutputCount { get; init; }
|
||||
public IReadOnlyList<ShaderVertexInput> VertexInputs { get; init; } = [];
|
||||
public uint PositionExportControl { get; init; }
|
||||
public bool SupportsClipDistance { get; init; } = true;
|
||||
public ShaderClipSpaceTransform ClipSpace { get; init; }
|
||||
|
||||
// The LDS the dispatch allocates (COMPUTE_PGM_RSRC2.LDS_SIZE), 0 when unknown.
|
||||
|
|
|
|||
|
|
@ -421,13 +421,16 @@ public sealed partial class GuestImageCacheTests
|
|||
harness.Shutdown();
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void NearCapacityReadback_ReusesTheSharedDownloadRing()
|
||||
[Theory]
|
||||
[InlineData(2047u)]
|
||||
[InlineData(2048u)]
|
||||
[InlineData(4096u)]
|
||||
public void Readback_HandlesNearCapacityAndOversizedImages(uint height)
|
||||
{
|
||||
if (!GatePrerequisites.Ready(_vulkan)) return;
|
||||
const uint width = 4096, height = 2047;
|
||||
const ulong size = (ulong)width * height * 4;
|
||||
using var harness = new CacheHarness(_vulkan, backingBytes: 40UL * 1024 * 1024);
|
||||
const uint width = 4096;
|
||||
var size = (ulong)width * height * 4;
|
||||
using var harness = new CacheHarness(_vulkan, backingBytes: size + 8UL * 1024 * 1024);
|
||||
var address = harness.MapBacked(size, ReadWrite);
|
||||
var request = LinearRequest(address, size, Format.R8G8B8A8Unorm, GuestPixelFormat.Bits8_8_8_8UNorm, GuestImageType.Color2D, new Extent3D(width, height, 1), 1, 4, 1);
|
||||
ulong[] samples = [0, size / 2, size - 4];
|
||||
|
|
|
|||
|
|
@ -73,6 +73,24 @@ public sealed class ImageRequestBuildersTests : IClassFixture<HeadlessVulkanFixt
|
|||
Assert.Equal(ImageRole.Texture, comparison.Request.Role);
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineData(GuestImageType.Color2D, 1u, true)]
|
||||
[InlineData(GuestImageType.Color3D, 32u, false)]
|
||||
[InlineData(GuestImageType.Color2DArray, 4u, false)]
|
||||
public void CompressedTexture_CarriesDccMetadataOnlyForSingleLayer2D(GuestImageType type, uint layers, bool expected)
|
||||
{
|
||||
const ulong metadata = 0x1_2000_0000;
|
||||
var words = RegisterWords.Texture(Base, GuestPixelFormat.Bits8_8_8_8UNorm, 32, 32, type, GuestTileMode.RenderTarget, layers: layers);
|
||||
words[6] |= (1u << 21) | (uint)(((metadata >> 8) & 0xFF) << 24);
|
||||
words[7] = (uint)(metadata >> 16);
|
||||
var shape = Sampled2D with { Volume = type == GuestImageType.Color3D, Arrayed = type == GuestImageType.Color2DArray };
|
||||
|
||||
var description = ImageRequestBuilders.Texture(words, shape).Request.Description;
|
||||
|
||||
Assert.Equal(expected ? MetadataKind.Dcc : MetadataKind.None, description.Metadata.Kind);
|
||||
Assert.Equal(expected ? metadata : 0UL, description.Metadata.Range.Address);
|
||||
}
|
||||
|
||||
private readonly HeadlessVulkan? _vulkan;
|
||||
|
||||
public ImageRequestBuildersTests(HeadlessVulkanFixture fixture) => _vulkan = fixture.Vulkan;
|
||||
|
|
|
|||
|
|
@ -336,6 +336,9 @@ internal sealed unsafe class HeadlessVulkan : IDisposable
|
|||
SamplerAnisotropy = baseFeatures.SamplerAnisotropy,
|
||||
ShaderStorageImageExtendedFormats = baseFeatures.ShaderStorageImageExtendedFormats,
|
||||
ShaderInt64 = baseFeatures.ShaderInt64,
|
||||
ShaderClipDistance = baseFeatures.ShaderClipDistance,
|
||||
ShaderCullDistance = baseFeatures.ShaderCullDistance,
|
||||
RobustBufferAccess = baseFeatures.RobustBufferAccess,
|
||||
};
|
||||
var priority = 1f;
|
||||
var queueInfo = new DeviceQueueCreateInfo
|
||||
|
|
|
|||
|
|
@ -0,0 +1,84 @@
|
|||
// Copyright (C) 2026 SharpEmu Emulator Project
|
||||
// SPDX-License-Identifier: GPL-2.0-or-later
|
||||
|
||||
using SharpEmu.Libs.Gpu.GpuCommands;
|
||||
using SharpEmu.Libs.Gpu.Pipelines;
|
||||
using SharpEmu.Libs.Gpu.Rendering;
|
||||
using SharpEmu.Libs.Tests.Gpu.Images;
|
||||
using SharpEmu.ShaderCompiler;
|
||||
using SharpEmu.ShaderCompiler.Resources;
|
||||
using SharpEmu.ShaderCompiler.Vulkan;
|
||||
using Xunit;
|
||||
using static SharpEmu.Libs.Tests.Gpu.Images.ImageCacheTestSupport;
|
||||
|
||||
namespace SharpEmu.Libs.Tests.VideoOut;
|
||||
|
||||
public sealed unsafe partial class RenderHostDeviceTests
|
||||
{
|
||||
[Fact]
|
||||
public void FullscreenStrip_DoesNotRasterizeTheVertexBeyondTheGuestDescriptor()
|
||||
{
|
||||
if (!Ready()) return;
|
||||
using var presenter = new PresenterUnderTest(_vulkan);
|
||||
presenter.LoadRenderingCommands();
|
||||
var harness = presenter.Harness;
|
||||
var target = harness.MapBacked(0x10000, ReadWrite);
|
||||
var vertices = harness.MapBacked(0x10000, ReadWrite);
|
||||
var words = RegisterWords.Color(target, Size, Size);
|
||||
// PLAYROOM draws four vertices from a three-record, 24-byte-stride descriptor.
|
||||
// The following record belongs to other guest data, not the fullscreen triangle.
|
||||
harness.Write(vertices, Bytes(
|
||||
-1f, -1f, 0f, 1f, 0f, 0f,
|
||||
3f, -1f, 0f, 1f, 2f, 0f,
|
||||
-1f, 3f, 0f, 1f, 0f, 2f,
|
||||
0f, 0f, BitConverter.UInt32BitsToSingle(9952), 0f, 0f, 0f));
|
||||
|
||||
var fetch = new Gen5ShaderInstruction(0, Gen5ShaderEncoding.Mubuf, "BufferLoadFormatXyzw", [], [], [],
|
||||
new Gen5BufferMemoryControl(4, 0, 0, 0, 0, true, false, false, false));
|
||||
var export = new Gen5ShaderInstruction(8, Gen5ShaderEncoding.Exp, "Exp", [],
|
||||
[Gen5Operand.Vector(0), Gen5Operand.Vector(1), Gen5Operand.Vector(2), Gen5Operand.Vector(3)], [],
|
||||
new Gen5ExportControl(12, 15, false, true, true));
|
||||
var parameter = new Gen5ShaderInstruction(16, Gen5ShaderEncoding.Exp, "Exp", [],
|
||||
[Gen5Operand.Vector(3), Gen5Operand.Vector(3), Gen5Operand.Vector(3), Gen5Operand.Vector(3)], [],
|
||||
new Gen5ExportControl(32, 15, false, true, true));
|
||||
var end = new Gen5ShaderInstruction(24, Gen5ShaderEncoding.Sopp, "SEndpgm", [], [], [], null);
|
||||
var plan = ShaderResourcePlan.Extract(new Gen5ShaderProgram(0, [fetch, export, parameter, end]), ShaderStage.Vertex, 1, 0, 0, new HashSet<uint> { 0 });
|
||||
var resources = ResourceMaterializer.ApplyTo(plan, ResourceSpecialization.Default(plan.Info));
|
||||
var request = new ShaderCompileRequest(plan, resources, BindingLayout.Allocate(resources.Info, [], false, false, false, 0))
|
||||
{
|
||||
EnableGraphicsSubgroupOperations = false,
|
||||
VertexInputs = [new ShaderVertexInput(0, 0, 4, 7, false, [])],
|
||||
};
|
||||
Assert.True(Gen5SpirvTranslator.TryCompileProgram(request, out var shader, out var error), error);
|
||||
|
||||
// Valid vertices interpolate white; an unculled out-of-bounds vertex can overwrite it.
|
||||
var fragment = new SpirvModuleBuilder();
|
||||
fragment.AddCapability(SpirvCapability.Shader);
|
||||
var voidType = fragment.TypeVoid();
|
||||
var vectorType = fragment.TypeVector(fragment.TypeFloat(32), 4);
|
||||
var input = fragment.AddGlobalVariable(fragment.TypePointer(SpirvStorageClass.Input, vectorType), SpirvStorageClass.Input);
|
||||
var output = fragment.AddGlobalVariable(fragment.TypePointer(SpirvStorageClass.Output, vectorType), SpirvStorageClass.Output);
|
||||
fragment.AddDecoration(input, SpirvDecoration.Location, 0);
|
||||
fragment.AddDecoration(output, SpirvDecoration.Location, 0);
|
||||
var main = fragment.BeginFunction(voidType, fragment.TypeFunction(voidType));
|
||||
fragment.AddLabel();
|
||||
fragment.AddStatement(SpirvOp.Store, output, fragment.AddInstruction(SpirvOp.Load, vectorType, input));
|
||||
fragment.AddStatement(SpirvOp.Return);
|
||||
fragment.EndFunction();
|
||||
fragment.AddEntryPoint(SpirvExecutionModel.Fragment, main, "main", [input, output]);
|
||||
fragment.AddExecutionMode(main, SpirvExecutionMode.OriginUpperLeft);
|
||||
var executor = new RenderExecutor(presenter.RenderHost,
|
||||
new FixedProgramProvider((IShaderPipelineHost)presenter.Instance, vertices,
|
||||
interpolationShader: fragment.Build(), vertexShader: shader.Spirv, vertexComponents: 4, vertexStride: 24));
|
||||
var banks = Banks(words);
|
||||
banks.UserConfig.PrimitiveType = 5;
|
||||
presenter.Run(() => executor.DrawAuto(1, banks,
|
||||
new DrawAutoArguments(0, 0, 4, 1, 0, 0, DrawOffsetSource.Packet)));
|
||||
presenter.Run(() => presenter.InvokeMethod("FlushBatchedGuestCommands"));
|
||||
harness.Finish();
|
||||
var pixels = harness.ReadImageBytes(TargetImage(presenter, words));
|
||||
Assert.All(Enumerable.Range(0, (int)(Size * Size)),
|
||||
index => Assert.Equal(uint.MaxValue, BitConverter.ToUInt32(pixels, index * 4)));
|
||||
harness.Shutdown();
|
||||
}
|
||||
}
|
||||
|
|
@ -143,11 +143,12 @@ public sealed unsafe partial class RenderHostDeviceTests : IClassFixture<Headles
|
|||
|
||||
// One float2 position program and one solid red pixel program over empty resource plans.
|
||||
private sealed class FixedProgramProvider(IShaderPipelineHost host, ulong vertexAddress, bool pushData = false,
|
||||
byte[]? interpolationShader = null, uint vertexCount = VertexCount) : IShaderPipelineProvider
|
||||
byte[]? interpolationShader = null, uint vertexCount = VertexCount,
|
||||
byte[]? vertexShader = null, uint vertexComponents = 2, uint vertexStride = VertexStride) : IShaderPipelineProvider
|
||||
{
|
||||
private const uint Float2Format = 64;
|
||||
private static readonly uint[] UserRegisters = [0, 1];
|
||||
private readonly ShaderProgramInfo _vertex = EmptyProgram(ShaderStageKind.Vertex, 1, fetchComponents: 2, userDataRegisters: pushData ? UserRegisters : null, pushDataStart: 2);
|
||||
private readonly ShaderProgramInfo _vertex = EmptyProgram(ShaderStageKind.Vertex, 1, fetchComponents: vertexComponents, userDataRegisters: pushData ? UserRegisters : null, pushDataStart: 2);
|
||||
private readonly ShaderProgramInfo _pixel = EmptyProgram(ShaderStageKind.Pixel, 2, userDataRegisters: pushData ? UserRegisters : null);
|
||||
private readonly ResourceSnapshot _snapshot = new() { UserData = pushData ? [0x11, 0x22] : [] };
|
||||
private ShaderProgram _vertexProgram;
|
||||
|
|
@ -179,7 +180,7 @@ public sealed unsafe partial class RenderHostDeviceTests : IClassFixture<Headles
|
|||
}
|
||||
|
||||
_vertexProgram = new ShaderProgram(1, host.CreateShaderModule(new VulkanCompiledGuestShader(
|
||||
CreatePositionVertexShader(interpolationShader is not null)), ShaderStage.Vertex, 1, 1));
|
||||
vertexShader ?? CreatePositionVertexShader(interpolationShader is not null)), ShaderStage.Vertex, 1, 1));
|
||||
_pixelProgram = new ShaderProgram(2, host.CreateShaderModule(new VulkanCompiledGuestShader(
|
||||
interpolationShader ?? SpirvFixedShaders.CreateSolidFragment(1f, 0f, 0f, 1f)), ShaderStage.Pixel, 2, 2));
|
||||
}
|
||||
|
|
@ -200,8 +201,8 @@ public sealed unsafe partial class RenderHostDeviceTests : IClassFixture<Headles
|
|||
Pixel = _pixelProgram,
|
||||
VertexInput = new VertexInputInfo
|
||||
{
|
||||
Buffers = [new VertexInputBuffer(vertexAddress, VertexStride, vertexCount)],
|
||||
Attributes = [new VertexAttributeResource(new BufferDescriptorWords((uint)vertexAddress, (uint)(vertexAddress >> 32) | (VertexStride << 16), vertexCount, Float2Format << 12), 0, 2, 0, 0, 0, 0)],
|
||||
Buffers = [new VertexInputBuffer(vertexAddress, vertexStride, vertexCount)],
|
||||
Attributes = [new VertexAttributeResource(new BufferDescriptorWords((uint)vertexAddress, (uint)(vertexAddress >> 32) | (vertexStride << 16), vertexCount, (vertexComponents == 4 ? 77u : Float2Format) << 12), 0, (int)vertexComponents, 0, 0, 0, 0)],
|
||||
Stage = new ShaderStageResources(_vertex, _snapshot),
|
||||
},
|
||||
PixelInput = new PixelInputInfo { InputCount = 0, Stage = new ShaderStageResources(_pixel, _snapshot) },
|
||||
|
|
|
|||
Loading…
Reference in New Issue