/// <summary> /// Gets a compute shader from the cache. /// </summary> /// <remarks> /// This automatically translates, compiles and adds the code to the cache if not present. /// </remarks> /// <param name="state">Current GPU state</param> /// <param name="gpuVa">GPU virtual address of the binary shader code</param> /// <param name="localSizeX">Local group size X of the computer shader</param> /// <param name="localSizeY">Local group size Y of the computer shader</param> /// <param name="localSizeZ">Local group size Z of the computer shader</param> /// <param name="localMemorySize">Local memory size of the compute shader</param> /// <param name="sharedMemorySize">Shared memory size of the compute shader</param> /// <returns>Compiled compute shader code</returns> public ShaderBundle GetComputeShader( GpuState state, ulong gpuVa, int localSizeX, int localSizeY, int localSizeZ, int localMemorySize, int sharedMemorySize) { bool isCached = _cpPrograms.TryGetValue(gpuVa, out List <ShaderBundle> list); if (isCached) { foreach (ShaderBundle cachedCpShader in list) { if (IsShaderEqual(cachedCpShader, gpuVa)) { return(cachedCpShader); } } } TranslatorContext[] shaderContexts = new TranslatorContext[1]; shaderContexts[0] = DecodeComputeShader( state, gpuVa, localSizeX, localSizeY, localSizeZ, localMemorySize, sharedMemorySize); bool isShaderCacheEnabled = _cacheManager != null; bool isShaderCacheReadOnly = false; Hash128 programCodeHash = default; GuestShaderCacheEntry[] shaderCacheEntries = null; // Current shader cache doesn't support bindless textures if (shaderContexts[0].UsedFeatures.HasFlag(FeatureFlags.Bindless)) { isShaderCacheEnabled = false; } if (isShaderCacheEnabled) { isShaderCacheReadOnly = _cacheManager.IsReadOnly; // Compute hash and prepare data for shader disk cache comparison. shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(_context.MemoryManager, shaderContexts); programCodeHash = CacheHelper.ComputeGuestHashFromCache(shaderCacheEntries); } ShaderBundle cpShader; // Search for the program hash in loaded shaders. if (!isShaderCacheEnabled || !_cpProgramsDiskCache.TryGetValue(programCodeHash, out cpShader)) { if (isShaderCacheEnabled) { Logger.Debug?.Print(LogClass.Gpu, $"Shader {programCodeHash} not in cache, compiling!"); } // The shader isn't currently cached, translate it and compile it. ShaderCodeHolder shader = TranslateShader(shaderContexts[0]); shader.HostShader = _context.Renderer.CompileShader(ShaderStage.Compute, shader.Program.Code); IProgram hostProgram = _context.Renderer.CreateProgram(new IShader[] { shader.HostShader }, null); hostProgram.CheckProgramLink(true); byte[] hostProgramBinary = HostShaderCacheEntry.Create(hostProgram.GetBinary(), new ShaderCodeHolder[] { shader }); cpShader = new ShaderBundle(hostProgram, shader); if (isShaderCacheEnabled) { _cpProgramsDiskCache.Add(programCodeHash, cpShader); if (!isShaderCacheReadOnly) { _cacheManager.SaveProgram(ref programCodeHash, CacheHelper.CreateGuestProgramDump(shaderCacheEntries), hostProgramBinary); } } } if (!isCached) { list = new List <ShaderBundle>(); _cpPrograms.Add(gpuVa, list); } list.Add(cpShader); return(cpShader); }
/// <summary> /// Invalidates all modified textures on the cache. /// </summary> /// <param name="state">Current GPU state (unused)</param> /// <param name="argument">Method call argument (unused)</param> private void InvalidateTextures(GpuState state, int argument) { TextureManager.Flush(); }
/// <summary> /// Sets the index buffer count. /// This also sets internal state that indicates that the next draw is an indexed draw. /// </summary> /// <param name="state">Current GPU state</param> /// <param name="argument">Method call argument</param> private void SetIndexBufferCount(GpuState state, int argument) { _drawIndexed = true; }
/// <summary> /// Dispatches compute work. /// </summary> /// <param name="state">Current GPU state</param> /// <param name="argument">Method call argument</param> public void Dispatch(GpuState state, int argument) { uint qmdAddress = (uint)state.Get <int>(MethodOffset.DispatchParamsAddress); var qmd = _context.MemoryAccessor.Read <ComputeQmd>((ulong)qmdAddress << 8); GpuVa shaderBaseAddress = state.Get <GpuVa>(MethodOffset.ShaderBaseAddress); ulong shaderGpuVa = shaderBaseAddress.Pack() + (uint)qmd.ProgramOffset; int localMemorySize = qmd.ShaderLocalMemoryLowSize + qmd.ShaderLocalMemoryHighSize; int sharedMemorySize = Math.Min(qmd.SharedMemorySize, _context.Capabilities.MaximumComputeSharedMemorySize); ComputeShader cs = ShaderCache.GetComputeShader( shaderGpuVa, qmd.CtaThreadDimension0, qmd.CtaThreadDimension1, qmd.CtaThreadDimension2, localMemorySize, sharedMemorySize); _context.Renderer.Pipeline.SetProgram(cs.HostProgram); var samplerPool = state.Get <PoolState>(MethodOffset.SamplerPoolState); TextureManager.SetComputeSamplerPool(samplerPool.Address.Pack(), samplerPool.MaximumId, qmd.SamplerIndex); var texturePool = state.Get <PoolState>(MethodOffset.TexturePoolState); TextureManager.SetComputeTexturePool(texturePool.Address.Pack(), texturePool.MaximumId); TextureManager.SetComputeTextureBufferIndex(state.Get <int>(MethodOffset.TextureBufferIndex)); ShaderProgramInfo info = cs.Shader.Program.Info; uint sbEnableMask = 0; uint ubEnableMask = 0; for (int index = 0; index < Constants.TotalCpUniformBuffers; index++) { if (!qmd.ConstantBufferValid(index)) { continue; } ubEnableMask |= 1u << index; ulong gpuVa = (uint)qmd.ConstantBufferAddrLower(index) | (ulong)qmd.ConstantBufferAddrUpper(index) << 32; ulong size = (ulong)qmd.ConstantBufferSize(index); BufferManager.SetComputeUniformBuffer(index, gpuVa, size); } for (int index = 0; index < info.SBuffers.Count; index++) { BufferDescriptor sb = info.SBuffers[index]; sbEnableMask |= 1u << sb.Slot; ulong sbDescAddress = BufferManager.GetComputeUniformBufferAddress(0); int sbDescOffset = 0x310 + sb.Slot * 0x10; sbDescAddress += (ulong)sbDescOffset; ReadOnlySpan <byte> sbDescriptorData = _context.PhysicalMemory.GetSpan(sbDescAddress, 0x10); SbDescriptor sbDescriptor = MemoryMarshal.Cast <byte, SbDescriptor>(sbDescriptorData)[0]; BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size); } ubEnableMask = 0; for (int index = 0; index < info.CBuffers.Count; index++) { ubEnableMask |= 1u << info.CBuffers[index].Slot; } BufferManager.SetComputeStorageBufferEnableMask(sbEnableMask); BufferManager.SetComputeUniformBufferEnableMask(ubEnableMask); var textureBindings = new TextureBindingInfo[info.Textures.Count]; for (int index = 0; index < info.Textures.Count; index++) { var descriptor = info.Textures[index]; Target target = GetTarget(descriptor.Type); if (descriptor.IsBindless) { textureBindings[index] = new TextureBindingInfo(target, descriptor.CbufOffset, descriptor.CbufSlot); } else { textureBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex); } } TextureManager.SetComputeTextures(textureBindings); var imageBindings = new TextureBindingInfo[info.Images.Count]; for (int index = 0; index < info.Images.Count; index++) { var descriptor = info.Images[index]; Target target = GetTarget(descriptor.Type); imageBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex); } TextureManager.SetComputeImages(imageBindings); BufferManager.CommitComputeBindings(); TextureManager.CommitComputeBindings(); _context.Renderer.Pipeline.DispatchCompute( qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth); UpdateShaderState(state); }
/// <summary> /// Updates host point size based on guest GPU state. /// </summary> /// <param name="state">Current GPU state</param> private void UpdatePointSizeState(GpuState state) { float size = state.Get <float>(MethodOffset.PointSize); _context.Renderer.Pipeline.SetPointSize(size); }
/// <summary> /// Updates host viewport transform and clipping state based on current GPU state. /// </summary> /// <param name="state">Current GPU state</param> private void UpdateViewportTransform(GpuState state) { DepthMode depthMode = state.Get <DepthMode>(MethodOffset.DepthMode); _context.Renderer.Pipeline.SetDepthMode(depthMode); YControl yControl = state.Get <YControl>(MethodOffset.YControl); bool flipY = yControl.HasFlag(YControl.NegateY); Origin origin = yControl.HasFlag(YControl.TriangleRastFlip) ? Origin.LowerLeft : Origin.UpperLeft; _context.Renderer.Pipeline.SetOrigin(origin); // The triangle rast flip flag only affects rasterization, the viewport is not flipped. // Setting the origin mode to upper left on the host, however, not only affects rasterization, // but also flips the viewport. // We negate the effects of flipping the viewport by flipping it again using the viewport swizzle. if (origin == Origin.UpperLeft) { flipY = !flipY; } Span <Viewport> viewports = stackalloc Viewport[Constants.TotalViewports]; for (int index = 0; index < Constants.TotalViewports; index++) { var transform = state.Get <ViewportTransform>(MethodOffset.ViewportTransform, index); var extents = state.Get <ViewportExtents> (MethodOffset.ViewportExtents, index); float x = transform.TranslateX - MathF.Abs(transform.ScaleX); float y = transform.TranslateY - MathF.Abs(transform.ScaleY); float width = MathF.Abs(transform.ScaleX) * 2; float height = MathF.Abs(transform.ScaleY) * 2; float scale = TextureManager.RenderTargetScale; if (scale != 1f) { x *= scale; y *= scale; width *= scale; height *= scale; } RectangleF region = new RectangleF(x, y, width, height); ViewportSwizzle swizzleX = transform.UnpackSwizzleX(); ViewportSwizzle swizzleY = transform.UnpackSwizzleY(); ViewportSwizzle swizzleZ = transform.UnpackSwizzleZ(); ViewportSwizzle swizzleW = transform.UnpackSwizzleW(); if (transform.ScaleX < 0) { swizzleX ^= ViewportSwizzle.NegativeFlag; } if (flipY) { swizzleY ^= ViewportSwizzle.NegativeFlag; } if (transform.ScaleY < 0) { swizzleY ^= ViewportSwizzle.NegativeFlag; } if (transform.ScaleZ < 0) { swizzleZ ^= ViewportSwizzle.NegativeFlag; } viewports[index] = new Viewport( region, swizzleX, swizzleY, swizzleZ, swizzleW, extents.DepthNear, extents.DepthFar); } _context.Renderer.Pipeline.SetViewports(0, viewports); }
/// <summary> /// Gets a texture descriptor used on the compute pipeline. /// </summary> /// <param name="state">Current GPU state</param> /// <param name="handle">Shader "fake" handle of the texture</param> /// <returns>The texture descriptor</returns> public TextureDescriptor GetComputeTextureDescriptor(GpuState state, int handle) { return(_cpBindingsManager.GetTextureDescriptor(state, 0, handle)); }
/// <summary> /// Finishes the draw call. /// This draws geometry on the bound buffers based on the current GPU state. /// </summary> /// <param name="state">Current GPU state</param> /// <param name="argument">Method call argument</param> private void DrawEnd(GpuState state, int argument) { var indexBuffer = state.Get <IndexBufferState>(MethodOffset.IndexBufferState); DrawEnd(state, indexBuffer.First, indexBuffer.Count); }
/// <summary> /// Finishes the draw call. /// This draws geometry on the bound buffers based on the current GPU state. /// </summary> /// <param name="state">Current GPU state</param> /// <param name="firstIndex">Index of the first index buffer element used on the draw</param> /// <param name="indexCount">Number of index buffer elements used on the draw</param> private void DrawEnd(GpuState state, int firstIndex, int indexCount) { ConditionalRenderEnabled renderEnable = GetRenderEnable(state); if (renderEnable == ConditionalRenderEnabled.False || _instancedDrawPending) { if (renderEnable == ConditionalRenderEnabled.False) { PerformDeferredDraws(); } _drawIndexed = false; if (renderEnable == ConditionalRenderEnabled.Host) { _context.Renderer.Pipeline.EndHostConditionalRendering(); } return; } UpdateState(state, firstIndex, indexCount); bool instanced = _vsUsesInstanceId || _isAnyVbInstanced; if (instanced) { _instancedDrawPending = true; _instancedIndexed = _drawIndexed; _instancedFirstIndex = firstIndex; _instancedFirstVertex = state.Get <int>(MethodOffset.FirstVertex); _instancedFirstInstance = state.Get <int>(MethodOffset.FirstInstance); _instancedIndexCount = indexCount; var drawState = state.Get <VertexBufferDrawState>(MethodOffset.VertexBufferDrawState); _instancedDrawStateFirst = drawState.First; _instancedDrawStateCount = drawState.Count; _drawIndexed = false; if (renderEnable == ConditionalRenderEnabled.Host) { _context.Renderer.Pipeline.EndHostConditionalRendering(); } return; } int firstInstance = state.Get <int>(MethodOffset.FirstInstance); int inlineIndexCount = _ibStreamer.GetAndResetInlineIndexCount(); if (inlineIndexCount != 0) { int firstVertex = state.Get <int>(MethodOffset.FirstVertex); BufferRange br = new BufferRange(_ibStreamer.GetInlineIndexBuffer(), 0, inlineIndexCount * 4); state.Channel.BufferManager.SetIndexBuffer(br, IndexType.UInt); _context.Renderer.Pipeline.DrawIndexed( inlineIndexCount, 1, firstIndex, firstVertex, firstInstance); } else if (_drawIndexed) { int firstVertex = state.Get <int>(MethodOffset.FirstVertex); _context.Renderer.Pipeline.DrawIndexed( indexCount, 1, firstIndex, firstVertex, firstInstance); } else { var drawState = state.Get <VertexBufferDrawState>(MethodOffset.VertexBufferDrawState); _context.Renderer.Pipeline.Draw( drawState.Count, 1, drawState.First, firstInstance); } _drawIndexed = false; if (renderEnable == ConditionalRenderEnabled.Host) { _context.Renderer.Pipeline.EndHostConditionalRendering(); } }
/// <summary> /// Performs a indexed draw with a low number of index buffer elements, /// while also pre-incrementing the current instance value. /// </summary> /// <param name="state">Current GPU state</param> /// <param name="argument">Method call argument</param> private void DrawIndexedSmallIncInstance2(GpuState state, int argument) { DrawIndexedSmallIncInstance(state, argument); }
/// <summary> /// Pushes one 32-bit index buffer element. /// </summary> /// <param name="state">Current GPU state</param> /// <param name="argument">Method call argument</param> private void VbElementU32(GpuState state, int argument) { _ibStreamer.VbElementU32(_context.Renderer, argument); }
/// <summary> /// Clears the current color and depth-stencil buffers. /// Which buffers should be cleared is also specified on the argument. /// </summary> /// <param name="state">Current GPU state</param> /// <param name="argument">Method call argument</param> private void Clear(GpuState state, int argument) { ConditionalRenderEnabled renderEnable = GetRenderEnable(state); if (renderEnable == ConditionalRenderEnabled.False) { return; } // Scissor and rasterizer discard also affect clears. if (state.QueryModified(MethodOffset.ScissorState)) { UpdateScissorState(state); } if (state.QueryModified(MethodOffset.RasterizeEnable)) { UpdateRasterizerState(state); } int index = (argument >> 6) & 0xf; UpdateRenderTargetState(state, useControl: false, singleUse: index); state.Channel.TextureManager.UpdateRenderTargets(); bool clearDepth = (argument & 1) != 0; bool clearStencil = (argument & 2) != 0; uint componentMask = (uint)((argument >> 2) & 0xf); if (componentMask != 0) { var clearColor = state.Get <ClearColors>(MethodOffset.ClearColors); ColorF color = new ColorF( clearColor.Red, clearColor.Green, clearColor.Blue, clearColor.Alpha); _context.Renderer.Pipeline.ClearRenderTargetColor(index, componentMask, color); } if (clearDepth || clearStencil) { float depthValue = state.Get <float>(MethodOffset.ClearDepthValue); int stencilValue = state.Get <int> (MethodOffset.ClearStencilValue); int stencilMask = 0; if (clearStencil) { stencilMask = state.Get <StencilTestState>(MethodOffset.StencilTestState).FrontMask; } _context.Renderer.Pipeline.ClearRenderTargetDepthStencil( depthValue, clearDepth, stencilValue, stencilMask); } UpdateRenderTargetState(state, useControl: true); if (renderEnable == ConditionalRenderEnabled.Host) { _context.Renderer.Pipeline.EndHostConditionalRendering(); } }
/// <summary> /// Finishes draw call. /// This draws geometry on the bound buffers based on the current GPU state. /// </summary> /// <param name="state">Current GPU state</param> /// <param name="argument">Method call argument</param> private void DrawEnd(GpuState state, int argument) { ConditionalRenderEnabled renderEnable = GetRenderEnable(state); if (renderEnable == ConditionalRenderEnabled.False || _instancedDrawPending) { if (renderEnable == ConditionalRenderEnabled.False) { PerformDeferredDraws(); } _drawIndexed = false; if (renderEnable == ConditionalRenderEnabled.Host) { _context.Renderer.Pipeline.EndHostConditionalRendering(); } return; } UpdateState(state); bool instanced = _vsUsesInstanceId || _isAnyVbInstanced; if (instanced) { _instancedDrawPending = true; _instancedIndexed = _drawIndexed; _instancedFirstIndex = _firstIndex; _instancedFirstVertex = state.Get <int>(MethodOffset.FirstVertex); _instancedFirstInstance = state.Get <int>(MethodOffset.FirstInstance); _instancedIndexCount = _indexCount; var drawState = state.Get <VertexBufferDrawState>(MethodOffset.VertexBufferDrawState); _instancedDrawStateFirst = drawState.First; _instancedDrawStateCount = drawState.Count; _drawIndexed = false; if (renderEnable == ConditionalRenderEnabled.Host) { _context.Renderer.Pipeline.EndHostConditionalRendering(); } return; } int firstInstance = state.Get <int>(MethodOffset.FirstInstance); if (_drawIndexed) { _drawIndexed = false; int firstVertex = state.Get <int>(MethodOffset.FirstVertex); _context.Renderer.Pipeline.DrawIndexed( _indexCount, 1, _firstIndex, firstVertex, firstInstance); } else { var drawState = state.Get <VertexBufferDrawState>(MethodOffset.VertexBufferDrawState); _context.Renderer.Pipeline.Draw( drawState.Count, 1, drawState.First, firstInstance); } if (renderEnable == ConditionalRenderEnabled.Host) { _context.Renderer.Pipeline.EndHostConditionalRendering(); } }
/// <summary> /// Gets a graphics shader program from the shader cache. /// This includes all the specified shader stages. /// </summary> /// <remarks> /// This automatically translates, compiles and adds the code to the cache if not present. /// </remarks> /// <param name="state">Current GPU state</param> /// <param name="addresses">Addresses of the shaders for each stage</param> /// <returns>Compiled graphics shader code</returns> public ShaderBundle GetGraphicsShader(GpuState state, ShaderAddresses addresses) { bool isCached = _gpPrograms.TryGetValue(addresses, out List <ShaderBundle> list); if (isCached) { foreach (ShaderBundle cachedGpShaders in list) { if (IsShaderEqual(cachedGpShaders, addresses)) { return(cachedGpShaders); } } } TranslatorContext[] shaderContexts = new TranslatorContext[Constants.ShaderStages + 1]; TransformFeedbackDescriptor[] tfd = GetTransformFeedbackDescriptors(state); TranslationFlags flags = DefaultFlags; if (tfd != null) { flags |= TranslationFlags.Feedback; } TranslationCounts counts = new TranslationCounts(); if (addresses.VertexA != 0) { shaderContexts[0] = DecodeGraphicsShader(state, counts, flags | TranslationFlags.VertexA, ShaderStage.Vertex, addresses.VertexA); } shaderContexts[1] = DecodeGraphicsShader(state, counts, flags, ShaderStage.Vertex, addresses.Vertex); shaderContexts[2] = DecodeGraphicsShader(state, counts, flags, ShaderStage.TessellationControl, addresses.TessControl); shaderContexts[3] = DecodeGraphicsShader(state, counts, flags, ShaderStage.TessellationEvaluation, addresses.TessEvaluation); shaderContexts[4] = DecodeGraphicsShader(state, counts, flags, ShaderStage.Geometry, addresses.Geometry); shaderContexts[5] = DecodeGraphicsShader(state, counts, flags, ShaderStage.Fragment, addresses.Fragment); bool isShaderCacheEnabled = _cacheManager != null; bool isShaderCacheReadOnly = false; Hash128 programCodeHash = default; GuestShaderCacheEntry[] shaderCacheEntries = null; // Current shader cache doesn't support bindless textures for (int i = 0; i < shaderContexts.Length; i++) { if (shaderContexts[i] != null && shaderContexts[i].UsedFeatures.HasFlag(FeatureFlags.Bindless)) { isShaderCacheEnabled = false; break; } } if (isShaderCacheEnabled) { isShaderCacheReadOnly = _cacheManager.IsReadOnly; // Compute hash and prepare data for shader disk cache comparison. shaderCacheEntries = CacheHelper.CreateShaderCacheEntries(_context.MemoryManager, shaderContexts); programCodeHash = CacheHelper.ComputeGuestHashFromCache(shaderCacheEntries, tfd); } ShaderBundle gpShaders; // Search for the program hash in loaded shaders. if (!isShaderCacheEnabled || !_gpProgramsDiskCache.TryGetValue(programCodeHash, out gpShaders)) { if (isShaderCacheEnabled) { Logger.Debug?.Print(LogClass.Gpu, $"Shader {programCodeHash} not in cache, compiling!"); } // The shader isn't currently cached, translate it and compile it. ShaderCodeHolder[] shaders = new ShaderCodeHolder[Constants.ShaderStages]; shaders[0] = TranslateShader(shaderContexts[1], shaderContexts[0]); shaders[1] = TranslateShader(shaderContexts[2]); shaders[2] = TranslateShader(shaderContexts[3]); shaders[3] = TranslateShader(shaderContexts[4]); shaders[4] = TranslateShader(shaderContexts[5]); List <IShader> hostShaders = new List <IShader>(); for (int stage = 0; stage < Constants.ShaderStages; stage++) { ShaderProgram program = shaders[stage]?.Program; if (program == null) { continue; } IShader hostShader = _context.Renderer.CompileShader(program.Stage, program.Code); shaders[stage].HostShader = hostShader; hostShaders.Add(hostShader); } IProgram hostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray(), tfd); hostProgram.CheckProgramLink(true); byte[] hostProgramBinary = HostShaderCacheEntry.Create(hostProgram.GetBinary(), shaders); gpShaders = new ShaderBundle(hostProgram, shaders); if (isShaderCacheEnabled) { _gpProgramsDiskCache.Add(programCodeHash, gpShaders); if (!isShaderCacheReadOnly) { _cacheManager.SaveProgram(ref programCodeHash, CacheHelper.CreateGuestProgramDump(shaderCacheEntries, tfd), hostProgramBinary); } } } if (!isCached) { list = new List <ShaderBundle>(); _gpPrograms.Add(addresses, list); } list.Add(gpShaders); return(gpShaders); }
/// <summary> /// Updates render targets (color and depth-stencil buffers) based on current render target state. /// </summary> /// <param name="state">Current GPU state</param> /// <param name="useControl">Use draw buffers information from render target control register</param> /// <param name="singleUse">If this is not -1, it indicates that only the given indexed target will be used.</param> private void UpdateRenderTargetState(GpuState state, bool useControl, int singleUse = -1) { var rtControl = state.Get <RtControl>(MethodOffset.RtControl); int count = useControl ? rtControl.UnpackCount() : Constants.TotalRenderTargets; var msaaMode = state.Get <TextureMsaaMode>(MethodOffset.RtMsaaMode); int samplesInX = msaaMode.SamplesInX(); int samplesInY = msaaMode.SamplesInY(); bool changedScale = false; for (int index = 0; index < Constants.TotalRenderTargets; index++) { int rtIndex = useControl ? rtControl.UnpackPermutationIndex(index) : index; var colorState = state.Get <RtColorState>(MethodOffset.RtColorState, rtIndex); if (index >= count || !IsRtEnabled(colorState)) { changedScale |= TextureManager.SetRenderTargetColor(index, null); continue; } Texture color = TextureManager.FindOrCreateTexture(colorState, samplesInX, samplesInY); changedScale |= TextureManager.SetRenderTargetColor(index, color); if (color != null) { color.SignalModified(); } } bool dsEnable = state.Get <Boolean32>(MethodOffset.RtDepthStencilEnable); Texture depthStencil = null; if (dsEnable) { var dsState = state.Get <RtDepthStencilState>(MethodOffset.RtDepthStencilState); var dsSize = state.Get <Size3D> (MethodOffset.RtDepthStencilSize); depthStencil = TextureManager.FindOrCreateTexture(dsState, dsSize, samplesInX, samplesInY); } changedScale |= TextureManager.SetRenderTargetDepthStencil(depthStencil); if (changedScale) { TextureManager.UpdateRenderTargetScale(singleUse); _context.Renderer.Pipeline.SetRenderTargetScale(TextureManager.RenderTargetScale); UpdateViewportTransform(state); UpdateScissorState(state); } if (depthStencil != null) { depthStencil.SignalModified(); } }
/// <summary> /// Dispatches compute work. /// </summary> /// <param name="state">Current GPU state</param> /// <param name="argument">Method call argument</param> public void Dispatch(GpuState state, int argument) { uint qmdAddress = (uint)state.Get <int>(MethodOffset.DispatchParamsAddress); var qmd = _context.MemoryManager.Read <ComputeQmd>((ulong)qmdAddress << 8); GpuVa shaderBaseAddress = state.Get <GpuVa>(MethodOffset.ShaderBaseAddress); ulong shaderGpuVa = shaderBaseAddress.Pack() + (uint)qmd.ProgramOffset; int localMemorySize = qmd.ShaderLocalMemoryLowSize + qmd.ShaderLocalMemoryHighSize; int sharedMemorySize = Math.Min(qmd.SharedMemorySize, _context.Capabilities.MaximumComputeSharedMemorySize); for (int index = 0; index < Constants.TotalCpUniformBuffers; index++) { if (!qmd.ConstantBufferValid(index)) { continue; } ulong gpuVa = (uint)qmd.ConstantBufferAddrLower(index) | (ulong)qmd.ConstantBufferAddrUpper(index) << 32; ulong size = (ulong)qmd.ConstantBufferSize(index); BufferManager.SetComputeUniformBuffer(index, gpuVa, size); } ShaderBundle cs = ShaderCache.GetComputeShader( state, shaderGpuVa, qmd.CtaThreadDimension0, qmd.CtaThreadDimension1, qmd.CtaThreadDimension2, localMemorySize, sharedMemorySize); _context.Renderer.Pipeline.SetProgram(cs.HostProgram); var samplerPool = state.Get <PoolState>(MethodOffset.SamplerPoolState); var texturePool = state.Get <PoolState>(MethodOffset.TexturePoolState); TextureManager.SetComputeSamplerPool(samplerPool.Address.Pack(), samplerPool.MaximumId, qmd.SamplerIndex); TextureManager.SetComputeTexturePool(texturePool.Address.Pack(), texturePool.MaximumId); TextureManager.SetComputeTextureBufferIndex(state.Get <int>(MethodOffset.TextureBufferIndex)); ShaderProgramInfo info = cs.Shaders[0].Info; for (int index = 0; index < info.CBuffers.Count; index++) { BufferDescriptor cb = info.CBuffers[index]; // NVN uses the "hardware" constant buffer for anything that is less than 8, // and those are already bound above. // Anything greater than or equal to 8 uses the emulated constant buffers. // They are emulated using global memory loads. if (cb.Slot < 8) { continue; } ulong cbDescAddress = BufferManager.GetComputeUniformBufferAddress(0); int cbDescOffset = 0x260 + (cb.Slot - 8) * 0x10; cbDescAddress += (ulong)cbDescOffset; SbDescriptor cbDescriptor = _context.PhysicalMemory.Read <SbDescriptor>(cbDescAddress); BufferManager.SetComputeUniformBuffer(cb.Slot, cbDescriptor.PackAddress(), (uint)cbDescriptor.Size); } for (int index = 0; index < info.SBuffers.Count; index++) { BufferDescriptor sb = info.SBuffers[index]; ulong sbDescAddress = BufferManager.GetComputeUniformBufferAddress(0); int sbDescOffset = 0x310 + sb.Slot * 0x10; sbDescAddress += (ulong)sbDescOffset; SbDescriptor sbDescriptor = _context.PhysicalMemory.Read <SbDescriptor>(sbDescAddress); BufferManager.SetComputeStorageBuffer(sb.Slot, sbDescriptor.PackAddress(), (uint)sbDescriptor.Size, sb.Flags); } BufferManager.SetComputeStorageBufferBindings(info.SBuffers); BufferManager.SetComputeUniformBufferBindings(info.CBuffers); var textureBindings = new TextureBindingInfo[info.Textures.Count]; for (int index = 0; index < info.Textures.Count; index++) { var descriptor = info.Textures[index]; Target target = ShaderTexture.GetTarget(descriptor.Type); textureBindings[index] = new TextureBindingInfo( target, descriptor.Binding, descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Flags); } TextureManager.SetComputeTextures(textureBindings); var imageBindings = new TextureBindingInfo[info.Images.Count]; for (int index = 0; index < info.Images.Count; index++) { var descriptor = info.Images[index]; Target target = ShaderTexture.GetTarget(descriptor.Type); Format format = ShaderTexture.GetFormat(descriptor.Format); imageBindings[index] = new TextureBindingInfo( target, format, descriptor.Binding, descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Flags); } TextureManager.SetComputeImages(imageBindings); TextureManager.CommitComputeBindings(); BufferManager.CommitComputeBindings(); _context.Renderer.Pipeline.DispatchCompute( qmd.CtaRasterWidth, qmd.CtaRasterHeight, qmd.CtaRasterDepth); _forceShaderUpdate = true; }
/// <summary> /// Updates host depth clamp state based on current GPU state. /// </summary> /// <param name="state">Current GPU state</param> private void UpdateDepthClampState(GpuState state) { ViewVolumeClipControl clip = state.Get <ViewVolumeClipControl>(MethodOffset.ViewVolumeClipControl); _context.Renderer.Pipeline.SetDepthClamp((clip & ViewVolumeClipControl.DepthClampDisabled) == 0); }
/// <summary> /// Executes a macro. /// </summary> /// <param name="index">Index of the macro</param> /// <param name="state">Current GPU state</param> public void CallMme(int index, GpuState state) { _macros[index].Execute(_macroCode, state); }
/// <summary> /// Updates host logical operation state, based on guest state. /// </summary> /// <param name="state">Current GPU state</param> public void UpdateLogicOpState(GpuState state) { LogicalOpState logicOpState = state.Get <LogicalOpState>(MethodOffset.LogicOpState); _context.Renderer.Pipeline.SetLogicOpState(logicOpState.Enable, logicOpState.LogicalOp); }
/// <summary> /// Performs a buffer to buffer, or buffer to texture copy. /// </summary> /// <param name="state">Current GPU state</param> /// <param name="argument">Method call argument</param> private void CopyBuffer(GpuState state, int argument) { var cbp = state.Get <CopyBufferParams>(MethodOffset.CopyBufferParams); var swizzle = state.Get <CopyBufferSwizzle>(MethodOffset.CopyBufferSwizzle); CopyFlags copyFlags = (CopyFlags)argument; bool srcLinear = copyFlags.HasFlag(CopyFlags.SrcLinear); bool dstLinear = copyFlags.HasFlag(CopyFlags.DstLinear); bool copy2D = copyFlags.HasFlag(CopyFlags.MultiLineEnable); bool remap = copyFlags.HasFlag(CopyFlags.RemapEnable); int size = cbp.XCount; if (size == 0) { return; } FlushUboUpdate(); if (copy2D) { // Buffer to texture copy. int srcBpp = remap ? swizzle.UnpackSrcComponentsCount() * swizzle.UnpackComponentSize() : 1; int dstBpp = remap ? swizzle.UnpackDstComponentsCount() * swizzle.UnpackComponentSize() : 1; var dst = state.Get <CopyBufferTexture>(MethodOffset.CopyBufferDstTexture); var src = state.Get <CopyBufferTexture>(MethodOffset.CopyBufferSrcTexture); var srcCalculator = new OffsetCalculator( src.Width, src.Height, cbp.SrcStride, srcLinear, src.MemoryLayout.UnpackGobBlocksInY(), src.MemoryLayout.UnpackGobBlocksInZ(), srcBpp); var dstCalculator = new OffsetCalculator( dst.Width, dst.Height, cbp.DstStride, dstLinear, dst.MemoryLayout.UnpackGobBlocksInY(), dst.MemoryLayout.UnpackGobBlocksInZ(), dstBpp); ulong srcBaseAddress = _context.MemoryManager.Translate(cbp.SrcAddress.Pack()); ulong dstBaseAddress = _context.MemoryManager.Translate(cbp.DstAddress.Pack()); (int srcBaseOffset, int srcSize) = srcCalculator.GetRectangleRange(src.RegionX, src.RegionY, cbp.XCount, cbp.YCount); (int dstBaseOffset, int dstSize) = dstCalculator.GetRectangleRange(dst.RegionX, dst.RegionY, cbp.XCount, cbp.YCount); ReadOnlySpan <byte> srcSpan = _context.PhysicalMemory.GetSpan(srcBaseAddress + (ulong)srcBaseOffset, srcSize, true); Span <byte> dstSpan = _context.PhysicalMemory.GetSpan(dstBaseAddress + (ulong)dstBaseOffset, dstSize).ToArray(); bool completeSource = IsTextureCopyComplete(cbp, src, srcLinear, srcBpp, cbp.SrcStride); bool completeDest = IsTextureCopyComplete(cbp, dst, dstLinear, dstBpp, cbp.DstStride); if (completeSource && completeDest) { Image.Texture target = TextureManager.FindTexture(dst, cbp, swizzle, dstLinear); if (target != null) { ReadOnlySpan <byte> data; if (srcLinear) { data = LayoutConverter.ConvertLinearStridedToLinear( target.Info.Width, target.Info.Height, 1, 1, cbp.SrcStride, target.Info.FormatInfo.BytesPerPixel, srcSpan); } else { data = LayoutConverter.ConvertBlockLinearToLinear( src.Width, src.Height, 1, target.Info.Levels, 1, 1, 1, srcBpp, src.MemoryLayout.UnpackGobBlocksInY(), src.MemoryLayout.UnpackGobBlocksInZ(), 1, new SizeInfo((int)target.Size), srcSpan); } target.SetData(data); target.SignalModified(); return; } else if (srcCalculator.LayoutMatches(dstCalculator)) { srcSpan.CopyTo(dstSpan); // No layout conversion has to be performed, just copy the data entirely. _context.PhysicalMemory.Write(dstBaseAddress + (ulong)dstBaseOffset, dstSpan); return; } } unsafe bool Convert <T>(Span <byte> dstSpan, ReadOnlySpan <byte> srcSpan) where T : unmanaged { fixed(byte *dstPtr = dstSpan, srcPtr = srcSpan) { byte *dstBase = dstPtr - dstBaseOffset; // Layout offset is relative to the base, so we need to subtract the span's offset. byte *srcBase = srcPtr - srcBaseOffset; for (int y = 0; y < cbp.YCount; y++) { srcCalculator.SetY(src.RegionY + y); dstCalculator.SetY(dst.RegionY + y); for (int x = 0; x < cbp.XCount; x++) { int srcOffset = srcCalculator.GetOffset(src.RegionX + x); int dstOffset = dstCalculator.GetOffset(dst.RegionX + x); *(T *)(dstBase + dstOffset) = *(T *)(srcBase + srcOffset); } } } return(true); } bool _ = srcBpp switch { 1 => Convert <byte>(dstSpan, srcSpan), 2 => Convert <ushort>(dstSpan, srcSpan), 4 => Convert <uint>(dstSpan, srcSpan), 8 => Convert <ulong>(dstSpan, srcSpan), 12 => Convert <Bpp12Pixel>(dstSpan, srcSpan), 16 => Convert <Vector128 <byte> >(dstSpan, srcSpan), _ => throw new NotSupportedException($"Unable to copy ${srcBpp} bpp pixel format.") }; _context.PhysicalMemory.Write(dstBaseAddress + (ulong)dstBaseOffset, dstSpan); } else { if (remap && swizzle.UnpackDstX() == BufferSwizzleComponent.ConstA && swizzle.UnpackDstY() == BufferSwizzleComponent.ConstA && swizzle.UnpackDstZ() == BufferSwizzleComponent.ConstA && swizzle.UnpackDstW() == BufferSwizzleComponent.ConstA && swizzle.UnpackSrcComponentsCount() == 1 && swizzle.UnpackDstComponentsCount() == 1 && swizzle.UnpackComponentSize() == 4) { // Fast path for clears when remap is enabled. BufferManager.ClearBuffer(cbp.DstAddress, (uint)size * 4, state.Get <uint>(MethodOffset.CopyBufferConstA)); } else { // TODO: Implement remap functionality. // Buffer to buffer copy. BufferManager.CopyBuffer(cbp.SrcAddress, cbp.DstAddress, (uint)size); } } }
/// <summary> /// Gets a texture descriptor used on the graphics pipeline. /// </summary> /// <param name="state">Current GPU state</param> /// <param name="stageIndex">Index of the shader stage where the texture is bound</param> /// <param name="handle">Shader "fake" handle of the texture</param> /// <returns>The texture descriptor</returns> public TextureDescriptor GetGraphicsTextureDescriptor(GpuState state, int stageIndex, int handle) { return(_gpBindingsManager.GetTextureDescriptor(state, stageIndex, handle)); }
/// <summary> /// Creates a new instance of the GPU state accessor for graphics shader translation. /// </summary> /// <param name="context">GPU context</param> /// <param name="state">Current GPU state</param> /// <param name="stageIndex">Graphics shader stage index (0 = Vertex, 4 = Fragment)</param> public GpuAccessor(GpuContext context, GpuState state, int stageIndex) { _context = context; _state = state; _stageIndex = stageIndex; }
/// <summary> /// Updates host state based on the current guest GPU state. /// </summary> /// <param name="state">Guest GPU state</param> private void UpdateState(GpuState state) { // Shaders must be the first one to be updated if modified, because // some of the other state depends on information from the currently // bound shaders. if (state.QueryModified(MethodOffset.ShaderBaseAddress, MethodOffset.ShaderState)) { UpdateShaderState(state); } if (state.QueryModified(MethodOffset.RtColorState, MethodOffset.RtDepthStencilState, MethodOffset.RtControl, MethodOffset.RtDepthStencilSize, MethodOffset.RtDepthStencilEnable)) { UpdateRenderTargetState(state, useControl: true); } if (state.QueryModified(MethodOffset.ScissorState)) { UpdateScissorState(state); } if (state.QueryModified(MethodOffset.DepthTestEnable, MethodOffset.DepthWriteEnable, MethodOffset.DepthTestFunc)) { UpdateDepthTestState(state); } if (state.QueryModified(MethodOffset.DepthMode, MethodOffset.ViewportTransform, MethodOffset.ViewportExtents)) { UpdateViewportTransform(state); } if (state.QueryModified(MethodOffset.DepthBiasState, MethodOffset.DepthBiasFactor, MethodOffset.DepthBiasUnits, MethodOffset.DepthBiasClamp)) { UpdateDepthBiasState(state); } if (state.QueryModified(MethodOffset.StencilBackMasks, MethodOffset.StencilTestState, MethodOffset.StencilBackTestState)) { UpdateStencilTestState(state); } // Pools. if (state.QueryModified(MethodOffset.SamplerPoolState, MethodOffset.SamplerIndex)) { UpdateSamplerPoolState(state); } if (state.QueryModified(MethodOffset.TexturePoolState)) { UpdateTexturePoolState(state); } // Input assembler state. if (state.QueryModified(MethodOffset.VertexAttribState)) { UpdateVertexAttribState(state); } if (state.QueryModified(MethodOffset.PointSize)) { UpdatePointSizeState(state); } if (state.QueryModified(MethodOffset.PrimitiveRestartState)) { UpdatePrimitiveRestartState(state); } if (state.QueryModified(MethodOffset.IndexBufferState)) { UpdateIndexBufferState(state); } if (state.QueryModified(MethodOffset.VertexBufferDrawState, MethodOffset.VertexBufferInstanced, MethodOffset.VertexBufferState, MethodOffset.VertexBufferEndAddress)) { UpdateVertexBufferState(state); } if (state.QueryModified(MethodOffset.FaceState)) { UpdateFaceState(state); } if (state.QueryModified(MethodOffset.RtColorMaskShared, MethodOffset.RtColorMask)) { UpdateRtColorMask(state); } if (state.QueryModified(MethodOffset.BlendIndependent, MethodOffset.BlendStateCommon, MethodOffset.BlendEnableCommon, MethodOffset.BlendEnable, MethodOffset.BlendState)) { UpdateBlendState(state); } CommitBindings(); }
/// <summary> /// Gets a graphics shader program from the shader cache. /// This includes all the specified shader stages. /// </summary> /// <remarks> /// This automatically translates, compiles and adds the code to the cache if not present. /// </remarks> /// <param name="state">Current GPU state</param> /// <param name="addresses">Addresses of the shaders for each stage</param> /// <returns>Compiled graphics shader code</returns> public ShaderBundle GetGraphicsShader(GpuState state, ShaderAddresses addresses) { bool isCached = _gpPrograms.TryGetValue(addresses, out List <ShaderBundle> list); if (isCached) { foreach (ShaderBundle cachedGpShaders in list) { if (IsShaderEqual(cachedGpShaders, addresses)) { return(cachedGpShaders); } } } ShaderCodeHolder[] shaders = new ShaderCodeHolder[Constants.ShaderStages]; var tfd = GetTransformFeedbackDescriptors(state); TranslationFlags flags = DefaultFlags; if (tfd != null) { flags |= TranslationFlags.Feedback; } if (addresses.VertexA != 0) { shaders[0] = TranslateGraphicsShader(state, flags, ShaderStage.Vertex, addresses.Vertex, addresses.VertexA); } else { shaders[0] = TranslateGraphicsShader(state, flags, ShaderStage.Vertex, addresses.Vertex); } shaders[1] = TranslateGraphicsShader(state, flags, ShaderStage.TessellationControl, addresses.TessControl); shaders[2] = TranslateGraphicsShader(state, flags, ShaderStage.TessellationEvaluation, addresses.TessEvaluation); shaders[3] = TranslateGraphicsShader(state, flags, ShaderStage.Geometry, addresses.Geometry); shaders[4] = TranslateGraphicsShader(state, flags, ShaderStage.Fragment, addresses.Fragment); List <IShader> hostShaders = new List <IShader>(); for (int stage = 0; stage < Constants.ShaderStages; stage++) { ShaderProgram program = shaders[stage]?.Program; if (program == null) { continue; } IShader hostShader = _context.Renderer.CompileShader(program); shaders[stage].HostShader = hostShader; hostShaders.Add(hostShader); } IProgram hostProgram = _context.Renderer.CreateProgram(hostShaders.ToArray(), tfd); ShaderBundle gpShaders = new ShaderBundle(hostProgram, shaders); if (!isCached) { list = new List <ShaderBundle>(); _gpPrograms.Add(addresses, list); } list.Add(gpShaders); return(gpShaders); }
/// <summary> /// Updates host shaders based on the guest GPU state. /// </summary> /// <param name="state">Current GPU state</param> private void UpdateShaderState(GpuState state) { ShaderAddresses addresses = new ShaderAddresses(); Span <ShaderAddresses> addressesSpan = MemoryMarshal.CreateSpan(ref addresses, 1); Span <ulong> addressesArray = MemoryMarshal.Cast <ShaderAddresses, ulong>(addressesSpan); ulong baseAddress = state.Get <GpuVa>(MethodOffset.ShaderBaseAddress).Pack(); for (int index = 0; index < 6; index++) { var shader = state.Get <ShaderState>(MethodOffset.ShaderState, index); if (!shader.UnpackEnable() && index != 1) { continue; } addressesArray[index] = baseAddress + shader.Offset; } GraphicsShader gs = ShaderCache.GetGraphicsShader(state, addresses); _vsUsesInstanceId = gs.Shaders[0]?.Program.Info.UsesInstanceId ?? false; for (int stage = 0; stage < Constants.ShaderStages; stage++) { ShaderProgramInfo info = gs.Shaders[stage]?.Program.Info; _currentProgramInfo[stage] = info; if (info == null) { continue; } var textureBindings = new TextureBindingInfo[info.Textures.Count]; for (int index = 0; index < info.Textures.Count; index++) { var descriptor = info.Textures[index]; Target target = GetTarget(descriptor.Type); if (descriptor.IsBindless) { textureBindings[index] = new TextureBindingInfo(target, descriptor.CbufSlot, descriptor.CbufOffset); } else { textureBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex); } } TextureManager.SetGraphicsTextures(stage, textureBindings); var imageBindings = new TextureBindingInfo[info.Images.Count]; for (int index = 0; index < info.Images.Count; index++) { var descriptor = info.Images[index]; Target target = GetTarget(descriptor.Type); imageBindings[index] = new TextureBindingInfo(target, descriptor.HandleIndex); } TextureManager.SetGraphicsImages(stage, imageBindings); uint sbEnableMask = 0; uint ubEnableMask = 0; for (int index = 0; index < info.SBuffers.Count; index++) { sbEnableMask |= 1u << info.SBuffers[index].Slot; } for (int index = 0; index < info.CBuffers.Count; index++) { ubEnableMask |= 1u << info.CBuffers[index].Slot; } BufferManager.SetGraphicsStorageBufferEnableMask(stage, sbEnableMask); BufferManager.SetGraphicsUniformBufferEnableMask(stage, ubEnableMask); } _context.Renderer.Pipeline.SetProgram(gs.HostProgram); }
/// <summary> /// Updates host state based on the current guest GPU state. /// </summary> /// <param name="state">Guest GPU state</param> private void UpdateState(GpuState state) { bool tfEnable = state.Get <Boolean32>(MethodOffset.TfEnable); if (!tfEnable && _prevTfEnable) { _context.Renderer.Pipeline.EndTransformFeedback(); _prevTfEnable = false; } // Shaders must be the first one to be updated if modified, because // some of the other state depends on information from the currently // bound shaders. if (state.QueryModified(MethodOffset.ShaderBaseAddress, MethodOffset.ShaderState) || _forceShaderUpdate) { _forceShaderUpdate = false; UpdateShaderState(state); } if (state.QueryModified(MethodOffset.TfBufferState)) { UpdateTfBufferState(state); } if (state.QueryModified(MethodOffset.ClipDistanceEnable)) { UpdateUserClipState(state); } if (state.QueryModified(MethodOffset.RasterizeEnable)) { UpdateRasterizerState(state); } if (state.QueryModified(MethodOffset.RtColorState, MethodOffset.RtDepthStencilState, MethodOffset.RtControl, MethodOffset.RtDepthStencilSize, MethodOffset.RtDepthStencilEnable)) { UpdateRenderTargetState(state, useControl: true); } if (state.QueryModified(MethodOffset.ScissorState)) { UpdateScissorState(state); } if (state.QueryModified(MethodOffset.ViewVolumeClipControl)) { UpdateDepthClampState(state); } if (state.QueryModified(MethodOffset.DepthTestEnable, MethodOffset.DepthWriteEnable, MethodOffset.DepthTestFunc)) { UpdateDepthTestState(state); } if (state.QueryModified(MethodOffset.DepthMode, MethodOffset.ViewportTransform, MethodOffset.ViewportExtents)) { UpdateViewportTransform(state); } if (state.QueryModified(MethodOffset.DepthBiasState, MethodOffset.DepthBiasFactor, MethodOffset.DepthBiasUnits, MethodOffset.DepthBiasClamp)) { UpdateDepthBiasState(state); } if (state.QueryModified(MethodOffset.StencilBackMasks, MethodOffset.StencilTestState, MethodOffset.StencilBackTestState)) { UpdateStencilTestState(state); } // Pools. if (state.QueryModified(MethodOffset.SamplerPoolState, MethodOffset.SamplerIndex)) { UpdateSamplerPoolState(state); } if (state.QueryModified(MethodOffset.TexturePoolState)) { UpdateTexturePoolState(state); } // Input assembler state. if (state.QueryModified(MethodOffset.VertexAttribState)) { UpdateVertexAttribState(state); } if (state.QueryModified(MethodOffset.PointSize, MethodOffset.VertexProgramPointSize, MethodOffset.PointSpriteEnable, MethodOffset.PointCoordReplace)) { UpdatePointState(state); } if (state.QueryModified(MethodOffset.PrimitiveRestartState)) { UpdatePrimitiveRestartState(state); } if (state.QueryModified(MethodOffset.IndexBufferState)) { UpdateIndexBufferState(state); } if (state.QueryModified(MethodOffset.VertexBufferDrawState, MethodOffset.VertexBufferInstanced, MethodOffset.VertexBufferState, MethodOffset.VertexBufferEndAddress)) { UpdateVertexBufferState(state); } if (state.QueryModified(MethodOffset.FaceState)) { UpdateFaceState(state); } if (state.QueryModified(MethodOffset.RtColorMaskShared, MethodOffset.RtColorMask)) { UpdateRtColorMask(state); } if (state.QueryModified(MethodOffset.BlendIndependent, MethodOffset.BlendConstant, MethodOffset.BlendStateCommon, MethodOffset.BlendEnableCommon, MethodOffset.BlendEnable, MethodOffset.BlendState)) { UpdateBlendState(state); } if (state.QueryModified(MethodOffset.LogicOpState)) { UpdateLogicOpState(state); } CommitBindings(); if (tfEnable && !_prevTfEnable) { _context.Renderer.Pipeline.BeginTransformFeedback(PrimitiveType.Convert()); _prevTfEnable = true; } }
/// <summary> /// Issues a texture barrier. /// This waits until previous texture writes from the GPU to finish, before /// performing new operations with said textures. /// This performs a per-tile wait, it is only valid if both the previous write /// and current access has the same access patterns. /// This may be faster than the regular barrier on tile-based rasterizers. /// </summary> /// <param name="state">Current GPU state (unused)</param> /// <param name="argument">Method call argument (unused)</param> private void TextureBarrierTiled(GpuState state, int argument) { _context.Renderer.Pipeline.TextureBarrierTiled(); }
/// <summary> /// Updates Rasterizer primitive discard state based on guest gpu state. /// </summary> /// <param name="state">Current GPU state</param> private void UpdateRasterizerState(GpuState state) { Boolean32 enable = state.Get <Boolean32>(MethodOffset.RasterizeEnable); _context.Renderer.Pipeline.SetRasterizerDiscard(!enable); }
/// <summary> /// Performs a indexed draw with a low number of index buffer elements. /// </summary> /// <param name="state">Current GPU state</param> /// <param name="argument">Method call argument</param> private void DrawIndexedSmall2(GpuState state, int argument) { DrawIndexedSmall(state, argument); }
/// <summary> /// Updates host shaders based on the guest GPU state. /// </summary> /// <param name="state">Current GPU state</param> private void UpdateShaderState(GpuState state) { ShaderAddresses addresses = new ShaderAddresses(); Span <ShaderAddresses> addressesSpan = MemoryMarshal.CreateSpan(ref addresses, 1); Span <ulong> addressesArray = MemoryMarshal.Cast <ShaderAddresses, ulong>(addressesSpan); ulong baseAddress = state.Get <GpuVa>(MethodOffset.ShaderBaseAddress).Pack(); for (int index = 0; index < 6; index++) { var shader = state.Get <ShaderState>(MethodOffset.ShaderState, index); if (!shader.UnpackEnable() && index != 1) { continue; } addressesArray[index] = baseAddress + shader.Offset; } ShaderBundle gs = state.Channel.MemoryManager.Physical.ShaderCache.GetGraphicsShader(state, addresses); byte oldVsClipDistancesWritten = _vsClipDistancesWritten; _vsUsesInstanceId = gs.Shaders[0]?.Info.UsesInstanceId ?? false; _vsClipDistancesWritten = gs.Shaders[0]?.Info.ClipDistancesWritten ?? 0; if (oldVsClipDistancesWritten != _vsClipDistancesWritten) { UpdateUserClipState(state); } int storageBufferBindingsCount = 0; int uniformBufferBindingsCount = 0; for (int stage = 0; stage < Constants.ShaderStages; stage++) { ShaderProgramInfo info = gs.Shaders[stage]?.Info; _currentProgramInfo[stage] = info; if (info == null) { state.Channel.TextureManager.SetGraphicsTextures(stage, Array.Empty <TextureBindingInfo>()); state.Channel.TextureManager.SetGraphicsImages(stage, Array.Empty <TextureBindingInfo>()); state.Channel.BufferManager.SetGraphicsStorageBufferBindings(stage, null); state.Channel.BufferManager.SetGraphicsUniformBufferBindings(stage, null); continue; } var textureBindings = new TextureBindingInfo[info.Textures.Count]; for (int index = 0; index < info.Textures.Count; index++) { var descriptor = info.Textures[index]; Target target = ShaderTexture.GetTarget(descriptor.Type); textureBindings[index] = new TextureBindingInfo( target, descriptor.Binding, descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Flags); } state.Channel.TextureManager.SetGraphicsTextures(stage, textureBindings); var imageBindings = new TextureBindingInfo[info.Images.Count]; for (int index = 0; index < info.Images.Count; index++) { var descriptor = info.Images[index]; Target target = ShaderTexture.GetTarget(descriptor.Type); Format format = ShaderTexture.GetFormat(descriptor.Format); imageBindings[index] = new TextureBindingInfo( target, format, descriptor.Binding, descriptor.CbufSlot, descriptor.HandleIndex, descriptor.Flags); } state.Channel.TextureManager.SetGraphicsImages(stage, imageBindings); state.Channel.BufferManager.SetGraphicsStorageBufferBindings(stage, info.SBuffers); state.Channel.BufferManager.SetGraphicsUniformBufferBindings(stage, info.CBuffers); if (info.SBuffers.Count != 0) { storageBufferBindingsCount = Math.Max(storageBufferBindingsCount, info.SBuffers.Max(x => x.Binding) + 1); } if (info.CBuffers.Count != 0) { uniformBufferBindingsCount = Math.Max(uniformBufferBindingsCount, info.CBuffers.Max(x => x.Binding) + 1); } } state.Channel.BufferManager.SetGraphicsStorageBufferBindingsCount(storageBufferBindingsCount); state.Channel.BufferManager.SetGraphicsUniformBufferBindingsCount(uniformBufferBindingsCount); _context.Renderer.Pipeline.SetProgram(gs.HostProgram); }