Example #1
0
        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));
 }
Example #4
0
        /// <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();
                }
            }
        }
Example #7
0
 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.");
 }
Example #8
0
 /// <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));
        }