public TVertex[] GetVertices <TVertex>(ConstructorInfo positionNormalTangentTexCoordCtor) where TVertex : struct { Assure.NotNull(positionNormalTangentTexCoordCtor); Assure.Equal(Indices.Length % 3, 0); Assure.NotEqual(TexCoords.Length, 0); TVertex[] result = new TVertex[NumVertices]; for (int tri = 0; tri < Indices.Length / 3; ++tri) { var c1 = Vertices[Indices[tri * 3 + 0]]; var c2 = Vertices[Indices[tri * 3 + 1]]; var c3 = Vertices[Indices[tri * 3 + 2]]; Vector3 v1 = Positions[c1.PositionIndex]; Vector2 w1 = TexCoords[c1.TexCoordIndex]; Vector3 v2 = Positions[c2.PositionIndex]; Vector2 w2 = TexCoords[c2.TexCoordIndex]; Vector3 v3 = Positions[c3.PositionIndex]; Vector2 w3 = TexCoords[c3.TexCoordIndex]; float x1 = v2.X - v1.X; float x2 = v3.X - v1.X; float y1 = v2.Y - v1.Y; float y2 = v3.Y - v1.Y; float z1 = v2.Z - v1.Z; float z2 = v3.Z - v1.Z; float s1 = w2.X - w1.X; float s2 = w3.X - w1.X; float t1 = w2.Y - w1.Y; float t2 = w3.Y - w1.Y; float r = 1f / (s1 * t2 - s2 * t1); Vector3 tan = new Vector3( (t2 * x1 - t1 * x2) * r, (t2 * y1 - t1 * y2) * r, (t2 * z1 - t1 * z2) * r ); result[tri * 3 + 0] = (TVertex)positionNormalTangentTexCoordCtor.Invoke(new object[] { v1, Normals.Length > 0 ? Normals[c1.NormalIndex] : Vector3.BACKWARD, tan, w1 }); result[tri * 3 + 1] = (TVertex)positionNormalTangentTexCoordCtor.Invoke(new object[] { v2, Normals.Length > 0 ? Normals[c2.NormalIndex] : Vector3.BACKWARD, tan, w2 }); result[tri * 3 + 2] = (TVertex)positionNormalTangentTexCoordCtor.Invoke(new object[] { v3, Normals.Length > 0 ? Normals[c3.NormalIndex] : Vector3.BACKWARD, tan, w3 }); } return(result); }
protected void CopyTo(BaseResource dest) { Assure.NotNull(dest); Assure.NotEqual(this, dest, "Can not copy to self."); Assure.Equal(Size, dest.Size, "Resources must be of equal size."); Assure.Equal(GetType(), dest.GetType(), "Resources must be of equal type."); dest.ThrowIfCannotBeCopyDestination(); lock (InstanceMutationLock) { if (IsDisposed) { Logger.Warn("Attempted copy manipulation from disposed resource of type: " + GetType().Name); return; } lock (dest.InstanceMutationLock) { if (dest.IsDisposed) { Logger.Warn("Attempted copy manipulation to disposed resource of type: " + GetType().Name); return; } InteropUtils.CallNative( NativeMethods.ResourceFactory_CopyResource, RenderingModule.DeviceContext, ResourceHandle, dest.ResourceHandle ).ThrowOnFailure(); } } }
internal GeometryCache(IVertexBuffer[] vertexComponentBuffers, string[] vertexComponentSemantics, ResourceFormat[] vertexComponentFormats, IndexBuffer indices, AlignedAllocation <uint> componentStartPointsAlloc, AlignedAllocation <uint> indexStartPointsAlloc, uint numModels, Type vertexType, int cacheID, Dictionary <string, ModelHandle> nameToHandleMap, bool orderFirst) { Assure.NotNull(vertexComponentBuffers); Assure.NotNull(vertexComponentSemantics); Assure.NotNull(vertexComponentFormats); Assure.NotNull(indices); Assure.Equal(vertexComponentBuffers.Length, vertexComponentSemantics.Length, "One or more vertex component arrays have different lengths."); Assure.Equal(vertexComponentFormats.Length, vertexComponentSemantics.Length, "One or more vertex component arrays have different lengths."); Assure.GreaterThan(vertexComponentBuffers.Length, 0, "Geometry cache with no vertex buffers is invalid."); Assure.NotNull(nameToHandleMap); this.vertexComponentBuffers = vertexComponentBuffers; this.vertexComponentSemantics = vertexComponentSemantics; this.vertexComponentFormats = vertexComponentFormats; this.indices = indices; this.componentStartPointsAlloc = componentStartPointsAlloc; this.indexStartPointsAlloc = indexStartPointsAlloc; this.componentStartPoints = (uint *)this.componentStartPointsAlloc.AlignedPointer; this.indexStartPoints = (uint *)this.indexStartPointsAlloc.AlignedPointer; this.NumModels = numModels; this.VertexType = vertexType; this.ID = cacheID; this.nameToHandleMap = nameToHandleMap; lock (staticMutationLock) { activeCaches.Add(ID, this); if (orderFirst) { activeCacheList.Insert(0, this); } else { activeCacheList.Add(this); } } this.createInputLayoutFunc = CreateInputLayout; GC.AddMemoryPressure(sizeof(uint) * 2 * (NumModels + 1)); }
/// <summary> /// Builds the <see cref="GeometryCache"/> with all the models that have previously been added with <see cref="AddModel"/>. /// This method may only be called once per GeometryCacheBuilder. /// </summary> /// <returns>A new <see cref="GeometryCache"/> containing 'baked' resource data for all the loaded models and an /// "instance pool", allowing model instances to be created and loaded in to the <see cref="Scene"/>.</returns> public unsafe GeometryCache Build() { lock (instanceMutationLock) { if (isBuilt) { throw new InvalidOperationException("Cache has already been built!"); } FieldInfo[] vertexComponents = typeof(TVertex).GetFields(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); if (vertexComponents.Length > VertexShader.MAX_VS_INPUT_BINDINGS) { throw new InvalidOperationException("Vertex type '" + typeof(TVertex).Name + "' has too many components. Maximum " + "permissible is " + VertexShader.MAX_VS_INPUT_BINDINGS + ", but the vertex type has " + vertexComponents.Length + " fields."); } else if (vertexComponents.Length == 0) { throw new InvalidOperationException("Vertex type '" + typeof(TVertex).Name + "' has no components. Please choose a type with " + "at least one field."); } if (vertexCounts.Count == 0) { throw new InvalidOperationException("Can not build empty geometry cache: Please add at least one model first!"); } if (modelNames.AnyDuplicates()) { throw new InvalidOperationException("No two models should have the same name."); } if (GeometryCache.CheckForModelNameClashes(modelNames)) { throw new InvalidOperationException("One or more model names added to this builder have already been used in other active geometry caches."); } IVertexBuffer[] vertexComponentBuffers = new IVertexBuffer[vertexComponents.Length]; string[] vertexComponentSemantics = new string[vertexComponents.Length]; ResourceFormat[] vertexComponentFormats = new ResourceFormat[vertexComponents.Length]; for (int i = 0; i < vertexComponents.Length; ++i) { FieldInfo component = vertexComponents[i]; if (!component.FieldType.IsBlittable()) { throw new InvalidOperationException("Invalid vertex component type: '" + component.FieldType.Name + "'."); } if (!component.HasCustomAttribute <VertexComponentAttribute>()) { throw new InvalidOperationException("Every field on given vertex type (" + typeof(TVertex).Name + ") must be annoted with " + "a " + typeof(VertexComponentAttribute).Name + "!"); } typeof(GeometryCacheBuilder <TVertex>) .GetMethod("FillComponentBuffer", BindingFlags.NonPublic | BindingFlags.Instance) // TODO replace with nameof() operator when C#6 is released .MakeGenericMethod(component.FieldType) .Invoke(this, new object[] { component, i, vertexComponentBuffers, vertexComponentSemantics, vertexComponentFormats }); } IndexBuffer indexBuffer = BufferFactory.NewIndexBuffer().WithInitialData(indices.ToArray()).WithUsage(ResourceUsage.Immutable); Assure.Equal(vertexCounts.Count, indexCounts.Count); AlignedAllocation <uint> componentStartPointsAlloc = AlignedAllocation <uint> .AllocArray( START_POINT_ARRAY_ALIGNMENT, (uint)vertexCounts.Count + 1U // Extra one so we can set the last value to one-past-the-end (performance improvement later on) ); AlignedAllocation <uint> indexStartPointsAlloc = AlignedAllocation <uint> .AllocArray( START_POINT_ARRAY_ALIGNMENT, (uint)vertexCounts.Count + 1U // Extra one so we can set the last value to one-past-the-end (performance improvement later on) ); uint *componentStartPtr = (uint *)componentStartPointsAlloc.AlignedPointer; uint *indexStartPtr = (uint *)indexStartPointsAlloc.AlignedPointer; uint vbCounter = 0U; uint ibCounter = 0U; for (int i = 0; i < vertexCounts.Count; ++i) { componentStartPtr[i] = vbCounter; indexStartPtr[i] = ibCounter; vbCounter += vertexCounts[i]; ibCounter += indexCounts[i]; } // Set the last two elements of each start-point array to one-past-the-last, so we don't have to test // for being the 'last' count later on componentStartPtr[vertexCounts.Count] = (uint)vertices.Count; indexStartPtr[vertexCounts.Count] = (uint)indices.Count; Dictionary <string, ModelHandle> modelNameToHandleMap = new Dictionary <string, ModelHandle>(); for (uint i = 0U; i < modelNames.Count; ++i) { modelNameToHandleMap.Add(modelNames[(int)i], new ModelHandle(cacheID, i)); } isBuilt = true; return(new GeometryCache( vertexComponentBuffers, vertexComponentSemantics, vertexComponentFormats, indexBuffer, componentStartPointsAlloc, indexStartPointsAlloc, (uint)vertexCounts.Count, typeof(TVertex), cacheID, modelNameToHandleMap, orderFirst )); } }
/// <summary> /// Performs a <see cref="ResourceUsage.Write"/> on this texture, copying the supplied data to the resource. /// </summary> /// <param name="data">The data to write. /// <see cref="ArraySlice{T}.Length">Length</see> vertices will be copied from the given /// array slice. The copy will start from the specified <see cref="ArraySlice{T}.Offset">Offset</see> in the /// contained array. /// </param> /// <param name="dataDesc">The region of the selected <paramref name="mipIndex"/> to write to. The /// <see cref="SubresourceBox.Volume"/> of the box must be equal to the <see cref="ArraySlice{T}.Length">Length</see> /// parameter of the supplied <paramref name="data"/>.</param> /// <param name="mipIndex">The mip level to write to. Only one mip level may be written to at a time. If this texture /// is not mipmapped, you must supply a value of <c>0U</c>.</param> /// <exception cref="ResourceOperationUnavailableException">Thrown if <see cref="BaseResource.CanDiscardWrite"/> is /// <c>false</c>.</exception> /// <exception cref="AssuranceFailedException">(Debug only) Thrown if the combination of supplied parameters would /// result in writing past the end of the texture in any dimension.</exception> public void Write(ArraySlice <TTexel> data, SubresourceBox dataDesc, uint mipIndex = 0U) { Assure.LessThan( mipIndex, NumMips, "Can not write to mip level " + mipIndex + ": Only " + NumMips + " present in texture." ); Assure.Equal( data.Length, dataDesc.Volume, "Invalid parameters: Data length must equal the write target region area." ); Assure.LessThanOrEqualTo( dataDesc.Left, Width, "Buffer overrun: Please ensure you are not attempting to write past the end of the texture." ); Assure.LessThanOrEqualTo( dataDesc.Right, Width, "Buffer overrun: Please ensure you are not attempting to write past the end of the texture." ); Assure.LessThanOrEqualTo( dataDesc.Top, Height, "Buffer overrun: Please ensure you are not attempting to write past the end of the texture." ); Assure.LessThanOrEqualTo( dataDesc.Bottom, Height, "Buffer overrun: Please ensure you are not attempting to write past the end of the texture." ); Assure.LessThanOrEqualTo( dataDesc.Front, Depth, "Buffer overrun: Please ensure you are not attempting to write past the end of the texture." ); Assure.LessThanOrEqualTo( dataDesc.Back, Depth, "Buffer overrun: Please ensure you are not attempting to write past the end of the texture." ); ThrowIfCannotWrite(); lock (InstanceMutationLock) { if (IsDisposed) { Logger.Warn("Attempted write manipulation on disposed resource of type: " + GetType().Name); return; } GCHandle pinnedDataHandle = GCHandle.Alloc(data.ContainingArray, GCHandleType.Pinned); try { if (Usage.ShouldUpdateSubresourceRegion()) { InteropUtils.CallNative( NativeMethods.ResourceFactory_UpdateSubresourceRegion, RenderingModule.DeviceContext, ResourceHandle, GetSubresourceIndex(mipIndex), (IntPtr)(&dataDesc), pinnedDataHandle.AddrOfPinnedObject() + (int)(data.Offset * TexelSizeBytes), dataDesc.Width * TexelSizeBytes, dataDesc.Width * dataDesc.Height * TexelSizeBytes ).ThrowOnFailure(); } else { Mutate_MapWrite(pinnedDataHandle, dataDesc, mipIndex); } } finally { pinnedDataHandle.Free(); } } }
/// <summary> /// Performs a <see cref="ResourceUsage.DiscardWrite"/> on this buffer. A discard-write is a faster write that first discards /// the old data, then writes the new data. /// </summary> /// <remarks> /// For 3D textures, discard-write operations always write the given data at the start of the selected mip. /// </remarks> /// <param name="data">The data to write. /// <see cref="ArraySlice{T}.Length">Length</see> vertices will be copied from the given /// array slice. The copy will start from the specified <see cref="ArraySlice{T}.Offset">Offset</see> in the /// contained array. /// </param> /// <param name="mipIndex">The mip level to write to. Only one mip level may be written to at a time. If this texture /// is not mipmapped, you must supply a value of <c>0U</c>.</param> /// <exception cref="ResourceOperationUnavailableException">Thrown if <see cref="BaseResource.CanDiscardWrite"/> is /// <c>false</c>.</exception> /// <exception cref="AssuranceFailedException">(Debug only) Thrown if the combination of supplied parameters would /// result in writing past the end of the texture in any dimension.</exception> public void DiscardWrite(ArraySlice <TTexel> data, uint mipIndex = 0U) { Assure.LessThan( mipIndex, NumMips, "Can not write to mip level " + mipIndex + ": Only " + NumMips + " present in texture." ); Assure.Equal( data.Length, SizeTexels, "Invalid array length (" + data.Length + "). Must be equal to SizeTexels (" + SizeTexels + ")." ); ThrowIfCannotDiscardWrite(); lock (InstanceMutationLock) { if (IsDisposed) { Logger.Warn("Attempted write manipulation on disposed resource of type: " + GetType().Name); return; } IntPtr outDataPtr; uint outRowStrideBytes, outSliceStrideBytes; InteropUtils.CallNative( NativeMethods.ResourceFactory_MapSubresource, RenderingModule.DeviceContext, ResourceHandle, GetSubresourceIndex(mipIndex), ResourceMapping.WriteDiscard, (IntPtr)(&outDataPtr), (IntPtr)(&outRowStrideBytes), (IntPtr)(&outSliceStrideBytes) ).ThrowOnFailure(); try { uint srcRowStrideBytes = MipWidth(mipIndex) * TexelSizeBytes; uint srcSliceStrideBytes = MipHeight(mipIndex) * srcRowStrideBytes; if (srcSliceStrideBytes == outSliceStrideBytes && srcRowStrideBytes == outRowStrideBytes) // Ultra fast copy { UnsafeUtils.CopyGenericArray( data, outDataPtr, TexelSizeBytes ); } else { GCHandle pinnedDataHandle = GCHandle.Alloc(data.ContainingArray, GCHandleType.Pinned); try { IntPtr srcDataPtr = pinnedDataHandle.AddrOfPinnedObject(); uint numSlices = MipDepth(mipIndex); uint numRows = MipHeight(mipIndex); int srcOffset = 0; for (uint s = 0; s < numSlices; ++s) { int dstOffset = (int)(s * outSliceStrideBytes); for (uint r = 0; r < numRows; ++r) { UnsafeUtils.MemCopy(srcDataPtr + srcOffset, outDataPtr + dstOffset, srcRowStrideBytes); dstOffset += (int)outRowStrideBytes; srcOffset += (int)srcRowStrideBytes; } } } finally { pinnedDataHandle.Free(); } } } finally { InteropUtils.CallNative( NativeMethods.ResourceFactory_UnmapSubresource, RenderingModule.DeviceContext, ResourceHandle, GetSubresourceIndex(mipIndex) ).ThrowOnFailure(); } } }
internal ConstantBufferBuilder(ResourceUsage usage, TConstants initialData) : base(usage, initialData) { Assure.True(typeof(TConstants).IsBlittable()); Assure.GreaterThan(UnsafeUtils.SizeOf <TConstants>(), 0); Assure.Equal(UnsafeUtils.SizeOf <TConstants>() % 16, 0, "Constant buffer size must be a multiple of 16 bytes."); }
/// <summary> /// Sets the value for the constant buffer represented by this binding. /// </summary> /// <param name="value">The value (serialized as a byte array) to set. The <see cref="ArraySlice{T}.Length"/> /// must be equal to <see cref="BufferSizeBytes"/>.</param> /// <seealso cref="SetValue(byte*)"/> public void SetValue(ArraySlice <byte> value) { Assure.Equal(value.Length, BufferSizeBytes, "Array length not equal to size of buffer."); UnsafeUtils.CopyGenericArray(value, CurValuePtr, sizeof(byte)); }
private ShaderTextureResourceView DoCreateView(uint firstMipIndex, uint numMips, ResourceFormat format) { if (firstMipIndex + numMips > NumMips || numMips == 0U) { throw new ArgumentOutOfRangeException("numMips"); } if ((PermittedBindings & GPUBindings.ReadableShaderResource) != GPUBindings.ReadableShaderResource) { throw new InvalidOperationException("Can not create an shader resource view to a resource that was created without the " + GPUBindings.ReadableShaderResource + " binding."); } ShaderResourceViewHandle outViewHandle; if (IsMultisampled) { Assure.Equal(firstMipIndex, 0U); Assure.Equal(numMips, 1U); if (IsArrayTexture) { InteropUtils.CallNative( NativeMethods.ResourceFactory_CreateSRVToTexture2DMSArray, RenderingModule.Device, (Texture2DResourceHandle)ResourceHandle, format, ArrayIndex, 1U, (IntPtr)(&outViewHandle) ).ThrowOnFailure(); } else { InteropUtils.CallNative( NativeMethods.ResourceFactory_CreateSRVToTexture2DMS, RenderingModule.Device, (Texture2DResourceHandle)ResourceHandle, format, (IntPtr)(&outViewHandle) ).ThrowOnFailure(); } } else { if (IsArrayTexture) { InteropUtils.CallNative( NativeMethods.ResourceFactory_CreateSRVToTexture2DArray, RenderingModule.Device, (Texture2DResourceHandle)ResourceHandle, format, firstMipIndex, numMips, ArrayIndex, 1U, (IntPtr)(&outViewHandle) ).ThrowOnFailure(); } else { InteropUtils.CallNative( NativeMethods.ResourceFactory_CreateSRVToTexture2D, RenderingModule.Device, (Texture2DResourceHandle)ResourceHandle, format, firstMipIndex, numMips, (IntPtr)(&outViewHandle) ).ThrowOnFailure(); } } return(new ShaderTextureResourceView(outViewHandle, this, firstMipIndex, numMips)); }