mirror of
https://github.com/space-wizards/RobustToolbox.git
synced 2026-02-14 19:29:36 +01:00
* oops
* fixes serialization il
* copytest
* typo & misc fixes
* 139 moment
* boxing
* mesa dum
* stuff
* goodbye bad friend
* last commit before the big (4) rewrite
* adds datanodes
* kills yamlobjserializer in favor of the new system
* adds more serializers, actually implements them & removes most of the last of the old system
* changed yamlfieldattribute namespace
* adds back iselfserialize
* refactors consts&flags
* renames everything to data(field/definition)
* adds afterserialization
* help
* dataclassgen
* fuggen help me mannen
* Fix most errors on content
* Fix engine errors except map loader
* maploader & misc fix
* misc fixes
* thing
* help
* refactors datanodes
* help me mannen
* Separate ITypeSerializer into reader and writer
* Convert all type serializers
* priority
* adds alot
* il fixes
* adds robustgen
* argh
* adds array & enum serialization
* fixes dataclasses
* adds vec2i / misc fixes
* fixes inheritance
* a very notcursed todo
* fixes some custom dataclasses
* push dis
* Remove data classes
* boutta box
* yes
* Add angle and regex serializer tests
* Make TypeSerializerTest abstract
* sets up ioc etc
* remove pushinheritance
* fixes
* Merge fixes, fix yaml hot reloading
* General fixes2
* Make enum serialization ignore case
* Fix the tag not being copied in data nodes
* Fix not properly serializing flag enums
* Fix component serialization on startup
* Implement ValueDataNode ToString
* Serialization IL fixes, fix return and string equality
* Remove async from prototype manager
* Make serializing unsupported node as enum exception more descriptive
* Fix serv3 tryread casting to serializer instead of reader
* Add constructor for invalid node type exception
* Temporary fix for SERV3: Turn populate delegate into regular code
* Fix not copying the data of non primitive types
* Fix not using the data definition found in copying
* Make ISerializationHooks require explicit implementations
* Add test for serialization inheritance
* Improve IsOverridenIn method
* Fix error message when a data definition is null
* Add method to cast a read value in Serv3Manager
* Rename IServ3Manager to ISerializationManager
* Rename usages of serv3manager, add generic copy method
* Fix IL copy method lookup
* Rename old usages of serv3manager
* Add ITypeCopier
* resistance is futile
* we will conquer this codebase
* Add copy method to all serializers
* Make primitive mismatch error message more descriptive
* bing bong im going to freacking heck
* oopsie moment
* hello are you interested in my wares
* does generic serializers under new architecture
* Convert every non generic serializer to the new format, general fixes
* Update usgaes of generic serializers, cleanup
* does some pushinheritance logic
* finishes pushinheritance FRAMEWORK
* shed
* Add box2, color and component registry serializer tests
* Create more deserialized types and store prototypes with their deserialized results
* Fixes and serializer updates
* Add serialization manager extensions
* adds pushinheritance
* Update all prototypes to have a parent and have consistent id/parent properties
* Fix grammar component serialization
* Add generic serializer tests
* thonk
* Add array serializer test
* Replace logger warning calls with exceptions
* fixes
* Move redundant methods to serialization manager extensions, cleanup
* Add array serialization
* fixes context
* more fixes
* argh
* inheritance
* this should do it
* fixes
* adds copiers & fixes some stuff
* copiers use context v1
* finishing copy context
* more context fixes
* Test fixes
* funky maps
* Fix server user interface component serialization
* Fix value tuple serialization
* Add copying for value types and arrays. Fix copy internal for primitives, enums and strings
* fixes
* fixes more stuff
* yes
* Make abstract/interface skips debugs instead of warnings
* Fix typo
* Make some dictionaries readonly
* Add checks for the serialization manager initializing and already being initialized
* Add base type required and usage for MeansDataDefinition and ImplicitDataDefinitionForInheritorsAttribute
* copy by ref
* Fix exception wording
* Update data field required summary with the new forbidden docs
* Use extension in map loader
* wanna erp
* Change serializing to not use il temporarily
* Make writing work with nullable types
* pushing
* check
* cuddling slaps HARD
* Add serialization priority test
* important fix
* a serialization thing
* serializer moment
* Add validation for some type serializers
* adds context
* moar context
* fixes
* Do the thing for appearance
* yoo lmao
* push haha pp
* Temporarily make copy delegate regular c# code
* Create deserialized component registry to handle not inheriting conflicting references
* YAML LINTER BABY
* ayes
* Fix sprite component norot not being default true like in latest master
* Remove redundant todos
* Add summary doc to every ISerializationManager method
* icon fixes
* Add skip hook argument to readers and copiers
* Merge fixes
* Fix ordering of arguments in read and copy reflection call
* Fix user interface components deserialization
* pew pew
* i am going to HECK
* Add MustUseReturnValue to copy-over methods
* Make serialization log calls use the same sawmill
* gamin
* Fix doc errors in ISerializationManager.cs
* goodbye brave soldier
* fixes
* WIP merge fixes and entity serialization
* aaaaaaaaaaaaaaa
* aaaaaaaaaaaaaaa
* adds inheritancebehaviour
* test/datafield fixes
* forgot that one
* adds more verbose validation
* This fixes the YAML hot reloading
* Replace yield break with Enumerable.Empty
* adds copiers
* aaaaaaaaaaaaa
* array fix
priority fix
misc fixes
* fix(?)
* fix.
* funny map serialization (wip)
* funny map serialization (wip)
* Add TODO
* adds proper info the validation
* Make yaml linter 5 times faster (~80% less execution time)
* Improves the error message for missing fields in the linter
* Include component name in unknown component type error node
* adds alwaysrelevant usa
* fixes mapsaving
* moved surpressor to analyzers proj
* warning cleanup & moves surpressor
* removes old msbuild targets
* Revert "Make yaml linter 5 times faster (~80% less execution time)"
This reverts commit 2ee4cc2c26.
* Add serialization to RobustServerSimulation and mock reflection methods
Fixes container tests
* Fix nullability warnings
* Improve yaml linter message feedback
* oops moment
* Add IEquatable, IComparable, ToString and operators to DataPosition
Rename it to NodeMark
Make it a readonly struct
* Remove try catch from enum parsing
* Make dependency management in serialization less bad
* Make dependencies an argument instead of a property on the serialization manager
* Clean up type serializers
* Improve validation messages and resourc epath checking
* Fix sprite error message
* reached perfection
Co-authored-by: Paul <ritter.paul1+git@googlemail.com>
Co-authored-by: DrSmugleaf <DrSmugleaf@users.noreply.github.com>
Co-authored-by: Vera Aguilera Puerto <zddm@outlook.es>
492 lines
17 KiB
C#
492 lines
17 KiB
C#
using System;
|
|
using System.Collections.Concurrent;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Text;
|
|
using OpenToolkit.Graphics.OpenGL4;
|
|
using Robust.Client.ResourceManagement;
|
|
using Robust.Shared.Maths;
|
|
using Robust.Shared.Utility;
|
|
using StencilOp = Robust.Client.Graphics.StencilOp;
|
|
|
|
namespace Robust.Client.Graphics.Clyde
|
|
{
|
|
internal partial class Clyde
|
|
{
|
|
private ClydeShaderInstance _defaultShader = default!;
|
|
|
|
private string _shaderLibrary = default!;
|
|
|
|
private string _shaderWrapCodeDefaultFrag = default!;
|
|
private string _shaderWrapCodeDefaultVert = default!;
|
|
|
|
private string _shaderWrapCodeRawFrag = default!;
|
|
private string _shaderWrapCodeRawVert = default!;
|
|
|
|
private readonly Dictionary<ClydeHandle, LoadedShader> _loadedShaders =
|
|
new();
|
|
|
|
private readonly Dictionary<ClydeHandle, LoadedShaderInstance> _shaderInstances =
|
|
new();
|
|
|
|
private readonly ConcurrentQueue<ClydeHandle> _deadShaderInstances = new();
|
|
|
|
private class LoadedShader
|
|
{
|
|
public GLShaderProgram Program = default!;
|
|
public bool HasLighting = true;
|
|
public ShaderBlendMode BlendMode;
|
|
public string? Name;
|
|
}
|
|
|
|
private class LoadedShaderInstance
|
|
{
|
|
public ClydeHandle ShaderHandle;
|
|
|
|
// TODO(perf): Maybe store these parameters not boxed with a tagged union.
|
|
public readonly Dictionary<string, object> Parameters = new();
|
|
|
|
public StencilParameters Stencil = StencilParameters.Default;
|
|
}
|
|
|
|
public ClydeHandle LoadShader(ParsedShader shader, string? name = null)
|
|
{
|
|
var (vertBody, fragBody) = GetShaderCode(shader);
|
|
|
|
var program = _compileProgram(vertBody, fragBody, BaseShaderAttribLocations, name);
|
|
|
|
if (_hasGLUniformBuffers)
|
|
{
|
|
program.BindBlock(UniProjViewMatrices, BindingIndexProjView);
|
|
program.BindBlock(UniUniformConstants, BindingIndexUniformConstants);
|
|
}
|
|
|
|
var loaded = new LoadedShader
|
|
{
|
|
Program = program,
|
|
HasLighting = shader.LightMode != ShaderLightMode.Unshaded,
|
|
BlendMode = shader.BlendMode,
|
|
Name = name
|
|
};
|
|
var handle = AllocRid();
|
|
_loadedShaders.Add(handle, loaded);
|
|
return handle;
|
|
}
|
|
|
|
public void ReloadShader(ClydeHandle handle, ParsedShader newShader)
|
|
{
|
|
var loaded = _loadedShaders[handle];
|
|
|
|
loaded.HasLighting = newShader.LightMode != ShaderLightMode.Unshaded;
|
|
loaded.BlendMode = newShader.BlendMode;
|
|
|
|
var (vertBody, fragBody) = GetShaderCode(newShader);
|
|
|
|
var program = _compileProgram(vertBody, fragBody, BaseShaderAttribLocations, loaded.Name);
|
|
|
|
loaded.Program.Delete();
|
|
|
|
loaded.Program = program;
|
|
|
|
if (_hasGLUniformBuffers)
|
|
{
|
|
program.BindBlock(UniProjViewMatrices, BindingIndexProjView);
|
|
program.BindBlock(UniUniformConstants, BindingIndexUniformConstants);
|
|
}
|
|
}
|
|
|
|
public ShaderInstance InstanceShader(ClydeHandle handle)
|
|
{
|
|
var newHandle = AllocRid();
|
|
var loaded = new LoadedShaderInstance
|
|
{
|
|
ShaderHandle = handle
|
|
};
|
|
var instance = new ClydeShaderInstance(newHandle, this);
|
|
_shaderInstances.Add(newHandle, loaded);
|
|
return instance;
|
|
}
|
|
|
|
private void LoadStockShaders()
|
|
{
|
|
_shaderLibrary = ReadEmbeddedShader("z-library.glsl");
|
|
|
|
_shaderWrapCodeDefaultFrag = ReadEmbeddedShader("base-default.frag");
|
|
_shaderWrapCodeDefaultVert = ReadEmbeddedShader("base-default.vert");
|
|
|
|
_shaderWrapCodeRawVert = ReadEmbeddedShader("base-raw.vert");
|
|
_shaderWrapCodeRawFrag = ReadEmbeddedShader("base-raw.frag");
|
|
|
|
var defaultLoadedShader = _resourceCache
|
|
.GetResource<ShaderSourceResource>("/Shaders/Internal/default-sprite.swsl").ClydeHandle;
|
|
|
|
_defaultShader = (ClydeShaderInstance) InstanceShader(defaultLoadedShader);
|
|
|
|
_queuedShader = _defaultShader.Handle;
|
|
}
|
|
|
|
private string ReadEmbeddedShader(string fileName)
|
|
{
|
|
var assembly = typeof(Clyde).Assembly;
|
|
using var stream = assembly.GetManifestResourceStream($"Robust.Client.Graphics.Clyde.Shaders.{fileName}")!;
|
|
DebugTools.AssertNotNull(stream);
|
|
using var reader = new StreamReader(stream, EncodingHelpers.UTF8);
|
|
return reader.ReadToEnd();
|
|
}
|
|
|
|
private GLShaderProgram _compileProgram(string vertexSource, string fragmentSource,
|
|
(string, uint)[] attribLocations, string? name = null)
|
|
{
|
|
GLShader? vertexShader = null;
|
|
GLShader? fragmentShader = null;
|
|
|
|
var versionHeader = "#version 140\n#define HAS_MOD\n";
|
|
|
|
if (_isGLES)
|
|
{
|
|
// GLES2 uses a different GLSL versioning scheme to desktop GL.
|
|
versionHeader = "#version 100\n#define HAS_VARYING_ATTRIBUTE\n";
|
|
if (_hasGLStandardDerivatives)
|
|
{
|
|
versionHeader += "#extension GL_OES_standard_derivatives : enable\n";
|
|
}
|
|
}
|
|
|
|
if (_hasGLStandardDerivatives)
|
|
{
|
|
versionHeader += "#define HAS_DFDX\n";
|
|
}
|
|
|
|
if (_hasGLFloatFramebuffers)
|
|
{
|
|
versionHeader += "#define HAS_FLOAT_TEXTURES\n";
|
|
}
|
|
|
|
if (_hasGLSrgb)
|
|
{
|
|
versionHeader += "#define HAS_SRGB\n";
|
|
}
|
|
|
|
if (_hasGLUniformBuffers)
|
|
{
|
|
versionHeader += "#define HAS_UNIFORM_BUFFERS\n";
|
|
}
|
|
|
|
vertexSource = versionHeader + "#define VERTEX_SHADER\n" + _shaderLibrary + vertexSource;
|
|
fragmentSource = versionHeader + "#define FRAGMENT_SHADER\n" + _shaderLibrary + fragmentSource;
|
|
|
|
try
|
|
{
|
|
try
|
|
{
|
|
vertexShader = new GLShader(this, ShaderType.VertexShader, vertexSource, name == null ? $"{name}-vertex" : null);
|
|
}
|
|
catch (ShaderCompilationException e)
|
|
{
|
|
File.WriteAllText("error.glsl", vertexSource);
|
|
throw new ShaderCompilationException(
|
|
"Failed to compile vertex shader, see inner for details (and error.glsl for formatted source).", e);
|
|
}
|
|
|
|
try
|
|
{
|
|
fragmentShader = new GLShader(this, ShaderType.FragmentShader, fragmentSource, name == null ? $"{name}-fragment" : null);
|
|
}
|
|
catch (ShaderCompilationException e)
|
|
{
|
|
File.WriteAllText("error.glsl", fragmentSource);
|
|
throw new ShaderCompilationException(
|
|
"Failed to compile fragment shader, see inner for details (and error.glsl for formatted source).", e);
|
|
}
|
|
|
|
var program = new GLShaderProgram(this, name);
|
|
program.Add(vertexShader);
|
|
program.Add(fragmentShader);
|
|
|
|
try
|
|
{
|
|
program.Link(attribLocations);
|
|
}
|
|
catch (ShaderCompilationException e)
|
|
{
|
|
program.Delete();
|
|
|
|
throw new ShaderCompilationException("Failed to link shaders. See inner for details.", e);
|
|
}
|
|
|
|
return program;
|
|
}
|
|
finally
|
|
{
|
|
vertexShader?.Delete();
|
|
fragmentShader?.Delete();
|
|
}
|
|
}
|
|
|
|
private (string vertBody, string fragBody) GetShaderCode(ParsedShader shader)
|
|
{
|
|
var headerUniforms = new StringBuilder();
|
|
|
|
foreach (var constant in shader.Constants.Values)
|
|
{
|
|
headerUniforms.AppendFormat("const {0} {1} = {2};\n", constant.Type.GetNativeType(), constant.Name,
|
|
constant.Value);
|
|
}
|
|
|
|
foreach (var uniform in shader.Uniforms.Values)
|
|
{
|
|
if (uniform.DefaultValue != null)
|
|
{
|
|
headerUniforms.AppendFormat("uniform {0} {1} = {2};\n", uniform.Type.GetNativeType(), uniform.Name,
|
|
uniform.DefaultValue);
|
|
}
|
|
else
|
|
{
|
|
headerUniforms.AppendFormat("uniform {0} {1};\n", uniform.Type.GetNativeType(), uniform.Name);
|
|
}
|
|
}
|
|
|
|
var varyingsFragment = new StringBuilder();
|
|
var varyingsVertex = new StringBuilder();
|
|
|
|
foreach (var (name, varying) in shader.Varyings)
|
|
{
|
|
varyingsFragment.AppendFormat("varying {0} {1};\n", varying.Type.GetNativeType(), name);
|
|
varyingsVertex.AppendFormat("varying {0} {1};\n", varying.Type.GetNativeType(), name);
|
|
}
|
|
|
|
ShaderFunctionDefinition? fragmentMain = null;
|
|
ShaderFunctionDefinition? vertexMain = null;
|
|
|
|
var functionsBuilder = new StringBuilder();
|
|
|
|
foreach (var function in shader.Functions)
|
|
{
|
|
if (function.Name == "fragment")
|
|
{
|
|
fragmentMain = function;
|
|
continue;
|
|
}
|
|
|
|
if (function.Name == "vertex")
|
|
{
|
|
vertexMain = function;
|
|
continue;
|
|
}
|
|
|
|
functionsBuilder.AppendFormat("{0} {1}(", function.ReturnType.GetNativeType(), function.Name);
|
|
var first = true;
|
|
foreach (var parameter in function.Parameters)
|
|
{
|
|
if (!first)
|
|
{
|
|
functionsBuilder.Append(", ");
|
|
}
|
|
|
|
first = false;
|
|
|
|
functionsBuilder.AppendFormat("{0} {1} {2}", parameter.Qualifiers.GetString(), parameter.Type.GetNativeType(),
|
|
parameter.Name);
|
|
}
|
|
|
|
functionsBuilder.AppendFormat(") {{\n{0}\n}}\n", function.Body);
|
|
}
|
|
|
|
var (wrapVert, wrapFrag) = shader.Preset switch
|
|
{
|
|
ShaderPreset.Default => (_shaderWrapCodeDefaultVert, _shaderWrapCodeDefaultFrag),
|
|
ShaderPreset.Raw => (_shaderWrapCodeRawVert, _shaderWrapCodeRawFrag),
|
|
_ => throw new NotSupportedException()
|
|
};
|
|
|
|
var vertexHeader = $"{headerUniforms}\n{varyingsVertex}\n{functionsBuilder}";
|
|
var fragmentHeader = $"{headerUniforms}\n{varyingsFragment}\n{functionsBuilder}";
|
|
|
|
// These are prefixed with comments because the syntax highlighter I'm using doesn't like the brackets.
|
|
// And it's producing a lot of squigly lines.
|
|
var vertexSource = wrapVert.Replace("// [SHADER_HEADER_CODE]", vertexHeader);
|
|
var fragmentSource = wrapFrag.Replace("// [SHADER_HEADER_CODE]", fragmentHeader);
|
|
|
|
if (vertexMain != null)
|
|
{
|
|
vertexSource = vertexSource.Replace("// [SHADER_CODE]", vertexMain.Body);
|
|
}
|
|
|
|
if (fragmentMain != null)
|
|
{
|
|
fragmentSource = fragmentSource.Replace("// [SHADER_CODE]", fragmentMain.Body);
|
|
}
|
|
|
|
return (vertexSource, fragmentSource);
|
|
}
|
|
|
|
private void FlushShaderInstanceDispose()
|
|
{
|
|
while (_deadShaderInstances.TryDequeue(out var handle))
|
|
{
|
|
_shaderInstances.Remove(handle);
|
|
}
|
|
}
|
|
|
|
private sealed class ClydeShaderInstance : ShaderInstance
|
|
{
|
|
public readonly ClydeHandle Handle;
|
|
public readonly Clyde Parent;
|
|
|
|
public ClydeShaderInstance(ClydeHandle handle, Clyde parent)
|
|
{
|
|
Handle = handle;
|
|
Parent = parent;
|
|
}
|
|
|
|
private protected override ShaderInstance DuplicateImpl()
|
|
{
|
|
var instanceData = Parent._shaderInstances[Handle];
|
|
var newData = new LoadedShaderInstance
|
|
{
|
|
ShaderHandle = instanceData.ShaderHandle
|
|
};
|
|
|
|
foreach (var (name, value) in instanceData.Parameters)
|
|
{
|
|
newData.Parameters.Add(name, value);
|
|
}
|
|
|
|
newData.Stencil = instanceData.Stencil;
|
|
|
|
var newHandle = Parent.AllocRid();
|
|
Parent._shaderInstances.Add(newHandle, newData);
|
|
return new ClydeShaderInstance(newHandle, Parent);
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
Parent._deadShaderInstances.Enqueue(Handle);
|
|
}
|
|
|
|
// TODO: Verify that parameters actually exist before assigning them like this.
|
|
|
|
private protected override void SetParameterImpl(string name, float value)
|
|
{
|
|
var data = Parent._shaderInstances[Handle];
|
|
data.Parameters[name] = value;
|
|
}
|
|
|
|
private protected override void SetParameterImpl(string name, Vector2 value)
|
|
{
|
|
var data = Parent._shaderInstances[Handle];
|
|
data.Parameters[name] = value;
|
|
}
|
|
|
|
private protected override void SetParameterImpl(string name, Vector3 value)
|
|
{
|
|
var data = Parent._shaderInstances[Handle];
|
|
data.Parameters[name] = value;
|
|
}
|
|
|
|
private protected override void SetParameterImpl(string name, Vector4 value)
|
|
{
|
|
var data = Parent._shaderInstances[Handle];
|
|
data.Parameters[name] = value;
|
|
}
|
|
|
|
private protected override void SetParameterImpl(string name, Color value)
|
|
{
|
|
var data = Parent._shaderInstances[Handle];
|
|
data.Parameters[name] = value;
|
|
}
|
|
|
|
private protected override void SetParameterImpl(string name, int value)
|
|
{
|
|
var data = Parent._shaderInstances[Handle];
|
|
data.Parameters[name] = value;
|
|
}
|
|
|
|
private protected override void SetParameterImpl(string name, Vector2i value)
|
|
{
|
|
var data = Parent._shaderInstances[Handle];
|
|
data.Parameters[name] = value;
|
|
}
|
|
|
|
private protected override void SetParameterImpl(string name, bool value)
|
|
{
|
|
var data = Parent._shaderInstances[Handle];
|
|
data.Parameters[name] = value;
|
|
}
|
|
|
|
private protected override void SetParameterImpl(string name, in Matrix3 value)
|
|
{
|
|
var data = Parent._shaderInstances[Handle];
|
|
data.Parameters[name] = value;
|
|
}
|
|
|
|
private protected override void SetParameterImpl(string name, in Matrix4 value)
|
|
{
|
|
var data = Parent._shaderInstances[Handle];
|
|
data.Parameters[name] = value;
|
|
}
|
|
|
|
private protected override void SetParameterImpl(string name, Texture value)
|
|
{
|
|
throw new NotImplementedException();
|
|
}
|
|
|
|
private protected override void SetStencilOpImpl(StencilOp op)
|
|
{
|
|
var data = Parent._shaderInstances[Handle];
|
|
data.Stencil.Op = op;
|
|
}
|
|
|
|
private protected override void SetStencilFuncImpl(StencilFunc func)
|
|
{
|
|
var data = Parent._shaderInstances[Handle];
|
|
data.Stencil.Func = func;
|
|
}
|
|
|
|
private protected override void SetStencilTestEnabledImpl(bool enabled)
|
|
{
|
|
var data = Parent._shaderInstances[Handle];
|
|
data.Stencil.Enabled = enabled;
|
|
}
|
|
|
|
private protected override void SetStencilRefImpl(int @ref)
|
|
{
|
|
var data = Parent._shaderInstances[Handle];
|
|
data.Stencil.Ref = @ref;
|
|
}
|
|
|
|
private protected override void SetStencilWriteMaskImpl(int mask)
|
|
{
|
|
var data = Parent._shaderInstances[Handle];
|
|
data.Stencil.WriteMask = mask;
|
|
}
|
|
|
|
private protected override void SetStencilReadMaskRefImpl(int mask)
|
|
{
|
|
var data = Parent._shaderInstances[Handle];
|
|
data.Stencil.ReadMask = mask;
|
|
}
|
|
}
|
|
|
|
private struct StencilParameters
|
|
{
|
|
public static readonly StencilParameters Default = new()
|
|
{
|
|
Enabled = false,
|
|
Ref = 0,
|
|
Op = StencilOp.Keep,
|
|
Func = StencilFunc.Always,
|
|
ReadMask = unchecked((int)uint.MaxValue),
|
|
WriteMask = unchecked((int)uint.MaxValue),
|
|
};
|
|
|
|
public bool Enabled;
|
|
public int Ref;
|
|
public int WriteMask;
|
|
public int ReadMask;
|
|
public StencilOp Op;
|
|
public StencilFunc Func;
|
|
}
|
|
}
|
|
}
|