public ModelInstanceHandle AllocateInstance(uint materialIndex, uint modelIndex, uint sceneLayerIndex, Transform initialTransform)
        {
            using (RenderingModule.RenderStateBarrier.AcquirePermit(withLock: instanceMutationLock)) {
                MIDArray           midArray = materialMap.GetOrCreate(materialIndex, createNewMIDArrayAct);
                ModelInstanceData *data     = midArray.Data;
                for (uint i = midArray.Length - 1U; i < midArray.Length; --i)
                {
                    if (!data[i].InUse)
                    {
                        data[i] = new ModelInstanceData(modelIndex, sceneLayerIndex, initialTransform);
                        return(new ModelInstanceHandle(this, materialIndex, i));
                    }
                }

                // MIDArray is full, so resize...
                uint newSize     = midArray.Length << 1;
                uint numBytes    = (uint)sizeof(ModelInstanceData) * newSize;
                uint oldNumBytes = (uint)sizeof(ModelInstanceData) * midArray.Length;
                if (midArray.Length >= MAX_SIZE_BEFORE_LINEAR_GROWTH)
                {
                    newSize = midArray.Length + LINEAR_GROWTH_AMOUNT;
                }
                ModelInstanceData *newData = (ModelInstanceData *)Marshal.AllocHGlobal(new IntPtr(numBytes));
                UnsafeUtils.MemCopy((IntPtr)data, (IntPtr)newData, oldNumBytes);
                Marshal.FreeHGlobal((IntPtr)data);
                UnsafeUtils.ZeroMem(((IntPtr)newData) + (int)oldNumBytes, numBytes - oldNumBytes);
                materialMap[materialIndex] = new MIDArray(newData, newSize);
                newData[midArray.Length]   = new ModelInstanceData(modelIndex, sceneLayerIndex, initialTransform);
                return(new ModelInstanceHandle(this, materialIndex, midArray.Length));
            }
        }
Ejemplo n.º 2
0
        public static RenderCommand DiscardWriteShaderConstantBuffer(ConstantBufferBinding binding, IntPtr valuePtr)
        {
            Assure.NotNull(binding);
            Assure.NotEqual(valuePtr, IntPtr.Zero, "valuePtr must not be IntPtr.Zero!");
            Assure.False(binding.IsDisposed || binding.GetBoundResource().IsDisposed, "Given binding or its resource was disposed.");
            IntPtr cbufferValPtr = AllocAndZeroTemp(binding.BufferSizeBytes);

            UnsafeUtils.MemCopy(valuePtr, cbufferValPtr, binding.BufferSizeBytes);
            return(new RenderCommand(RenderCommandInstruction.CBDiscardWrite, (IntPtr)binding.GetBoundResource().ResourceHandle, cbufferValPtr, binding.BufferSizeBytes));
        }
Ejemplo n.º 3
0
        /// <summary>
        /// Performs a <see cref="ResourceUsage.StagingRead"/> on this texture, copying all the data from every mip level and
        /// concatenating it in to a single <typeparamref name="TTexel"/> array.
        /// </summary>
        /// <returns>An array of all texels in this resource, ordered by ascending mip level.</returns>
        /// <exception cref="ResourceOperationUnavailableException">Thrown if <see cref="BaseResource.CanRead"/> is
        /// <c>false</c>.</exception>
        public override TTexel[] ReadAll()
        {
            ThrowIfCannotRead();
            return(LosgapSystem.InvokeOnMaster(() => {
                TTexel[] result = new TTexel[SizeTexels];

                lock (InstanceMutationLock) {
                    if (IsDisposed)
                    {
                        Logger.Warn("Attempted read manipulation on disposed resource of type: " + GetType().Name);
                        return result;
                    }
                    GCHandle pinnedResult = GCHandle.Alloc(result, GCHandleType.Pinned);
                    try {
                        int offsetBytes = 0;
                        for (uint i = 0; i < NumMips; ++i)
                        {
                            IntPtr outDataPtr;
                            uint outRowStrideBytes, outSliceStrideBytes;
                            InteropUtils.CallNative(
                                NativeMethods.ResourceFactory_MapSubresource,
                                RenderingModule.DeviceContext,
                                ResourceHandle,
                                GetSubresourceIndex(i),
                                ResourceMapping.Read,
                                (IntPtr)(&outDataPtr),
                                (IntPtr)(&outRowStrideBytes),
                                (IntPtr)(&outSliceStrideBytes)
                                ).ThrowOnFailure();

                            try {
                                uint numBytes = MipWidth(i) * TexelSizeBytes;
                                UnsafeUtils.MemCopy(outDataPtr, pinnedResult.AddrOfPinnedObject() + offsetBytes, numBytes);
                                offsetBytes += (int)numBytes;
                            }
                            finally {
                                InteropUtils.CallNative(
                                    NativeMethods.ResourceFactory_UnmapSubresource,
                                    RenderingModule.DeviceContext,
                                    ResourceHandle,
                                    GetSubresourceIndex(i)
                                    ).ThrowOnFailure();
                            }
                        }
                    }
                    finally {
                        pinnedResult.Free();
                    }
                }

                return result;
            }));
        }
Ejemplo n.º 4
0
        private unsafe void ResizeList()
        {
            uint newListLen = CurListLen + LIST_SIZE_INCREMENT;

            GC.RemoveMemoryPressure(sizeof(RenderCommand) * CurListLen);
            AlignedAllocation <RenderCommand> newListSpace = AlignedAllocation <RenderCommand> .AllocArray(LIST_ALIGNMENT, newListLen);

            GC.AddMemoryPressure(sizeof(RenderCommand) * CurListLen);
            UnsafeUtils.MemCopy(RenderCommandList.AlignedPointer, newListSpace.AlignedPointer, (uint)sizeof(RenderCommand) * CurListLen);
            CurListLen = newListLen;

            RenderCommandList.Dispose();

            RenderCommandList = newListSpace;
        }
Ejemplo n.º 5
0
        public static RenderCommand DiscardWriteShaderConstantBuffer <T>(Buffer <T> buffer, ArraySlice <T> data, uint sizeofT) where T : struct
        {
            var      dataSize         = data.Length * sizeofT;
            IntPtr   cbufferValPtr    = AllocAndZeroTemp(dataSize);
            GCHandle pinnedDataHandle = GCHandle.Alloc(data.ContainingArray, GCHandleType.Pinned);

            try {
                UnsafeUtils.MemCopy(pinnedDataHandle.AddrOfPinnedObject(), cbufferValPtr, dataSize);
            }
            finally {
                pinnedDataHandle.Free();
            }

            return(new RenderCommand(RenderCommandInstruction.BufferWrite, (IntPtr)buffer.ResourceHandle, cbufferValPtr, dataSize));
        }
        private void Mutate_MapWrite(IntPtr dataPtr, uint numBytesToWrite, uint writeOffset, ResourceMapping writeType)
        {
            lock (InstanceMutationLock) {
                if (IsDisposed)
                {
                    Logger.Warn("Attempted write manipulation on disposed resource of type: " + GetType().Name);
                    return;
                }
                IntPtr outDataPtr;
                uint   outUnused;

                char *failReason = stackalloc char[InteropUtils.MAX_INTEROP_FAIL_REASON_STRING_LENGTH + 1];
                bool  success    = NativeMethods.ResourceFactory_MapSubresource(
                    (IntPtr)failReason,
                    RenderingModule.DeviceContext,
                    ResourceHandle,
                    0U,
                    writeType,
                    (IntPtr)(&outDataPtr),
                    (IntPtr)(&outUnused),
                    (IntPtr)(&outUnused)
                    );
                if (!success)
                {
                    throw new NativeOperationFailedException(Marshal.PtrToStringUni((IntPtr)failReason));
                }

                UnsafeUtils.MemCopy(dataPtr, outDataPtr + (int)writeOffset, numBytesToWrite);

                char *failReason2 = stackalloc char[InteropUtils.MAX_INTEROP_FAIL_REASON_STRING_LENGTH + 1];
                bool  success2    = NativeMethods.ResourceFactory_UnmapSubresource(
                    (IntPtr)failReason,
                    RenderingModule.DeviceContext,
                    ResourceHandle,
                    0U
                    );
                if (!success2)
                {
                    throw new NativeOperationFailedException(Marshal.PtrToStringUni((IntPtr)failReason2));
                }
            }
        }
Ejemplo n.º 7
0
        private void Mutate_MapWrite(GCHandle pinnedDataHandle, SubresourceBox dataDesc, uint mipIndex)
        {
            LosgapSystem.InvokeOnMasterAsync(() => {
                IntPtr dstDataPtr;
                uint outRowStrideBytes, outSliceStrideBytes;
                InteropUtils.CallNative(
                    NativeMethods.ResourceFactory_MapSubresource,
                    RenderingModule.DeviceContext,
                    ResourceHandle,
                    GetSubresourceIndex(mipIndex),
                    ResourceMapping.Write,
                    (IntPtr)(&dstDataPtr),
                    (IntPtr)(&outRowStrideBytes),
                    (IntPtr)(&outSliceStrideBytes)
                    ).ThrowOnFailure();

                try {
                    IntPtr srcDataPtr = pinnedDataHandle.AddrOfPinnedObject();
                    for (uint srcSlice = 0U, dstSliceStart = outSliceStrideBytes * dataDesc.Front;
                         srcSlice < dataDesc.Depth;
                         ++srcSlice, dstSliceStart += outSliceStrideBytes)
                    {
                        for (uint srcRow = 0U, dstRowStart = outRowStrideBytes * dataDesc.Top;
                             srcRow < dataDesc.Height;
                             ++srcRow, dstRowStart += outRowStrideBytes)
                        {
                            UnsafeUtils.MemCopy(srcDataPtr + (int)((dataDesc.Width * srcRow + dataDesc.Height * srcSlice) * TexelSizeBytes),
                                                dstDataPtr + (int)(dataDesc.Left + dstRowStart + dstSliceStart), dataDesc.Width * TexelSizeBytes);
                        }
                    }
                }
                finally {
                    InteropUtils.CallNative(
                        NativeMethods.ResourceFactory_UnmapSubresource,
                        RenderingModule.DeviceContext,
                        ResourceHandle,
                        GetSubresourceIndex(mipIndex)
                        ).ThrowOnFailure();
                }
            });
        }
Ejemplo n.º 8
0
        /// <summary>
        /// Performs a <see cref="ResourceUsage.StagingRead"/> on this texture,
        /// returning a view of the texel data at the given <paramref name="mipIndex"/>.
        /// </summary>
        /// <param name="mipIndex">The mip index to read data from. Must be less than <see cref="ITexture.NumMips"/>.</param>
        /// <returns>A <see cref="TexelArray3D{TTexel}"/> of the data.</returns>
        /// <exception cref="ResourceOperationUnavailableException">Thrown if <see cref="BaseResource.CanRead"/> is
        /// <c>false</c>.</exception>
        public TexelArray3D <TTexel> Read(uint mipIndex)
        {
            Assure.LessThan(
                mipIndex, NumMips,
                "Can not read from mip level " + mipIndex + ": Only " + NumMips + " present in texture."
                );

            ThrowIfCannotRead();

            TTexel[] data = LosgapSystem.InvokeOnMaster(() => {
                TTexel[] result = new TTexel[GetSizeTexels(mipIndex)];

                lock (InstanceMutationLock) {
                    if (IsDisposed)
                    {
                        Logger.Warn("Attempted read manipulation on disposed resource of type: " + GetType().Name);
                        return(result);
                    }
                    GCHandle pinnedResult = GCHandle.Alloc(result, GCHandleType.Pinned);
                    try {
                        IntPtr outDataPtr;
                        uint outRowStrideBytes, outSliceStrideBytes;
                        InteropUtils.CallNative(
                            NativeMethods.ResourceFactory_MapSubresource,
                            RenderingModule.DeviceContext,
                            ResourceHandle,
                            GetSubresourceIndex(mipIndex),
                            ResourceMapping.Read,
                            (IntPtr)(&outDataPtr),
                            (IntPtr)(&outRowStrideBytes),
                            (IntPtr)(&outSliceStrideBytes)
                            ).ThrowOnFailure();

                        try {
                            uint numRows        = MipHeight(mipIndex);
                            uint numSlices      = MipDepth(mipIndex);
                            uint rowSizeBytes   = MipWidth(mipIndex) * TexelSizeBytes;
                            uint sliceSizeBytes = MipHeight(mipIndex) * rowSizeBytes;
                            if (sliceSizeBytes == outSliceStrideBytes && rowSizeBytes == outRowStrideBytes)                               // If we can copy the whole lot in one go, do it
                            {
                                UnsafeUtils.MemCopy(outDataPtr,
                                                    pinnedResult.AddrOfPinnedObject(), sliceSizeBytes * numSlices);
                            }
                            else                               // Otherwise, copy slice-by-slice and row-by-row
                            {
                                for (uint s = 0; s < numSlices; ++s)
                                {
                                    int dstOffsetBytes = (int)(s * sliceSizeBytes);
                                    int srcOffsetBytes = (int)(s * outSliceStrideBytes);
                                    for (uint r = 0; r < numRows; ++r)
                                    {
                                        UnsafeUtils.MemCopy(outDataPtr + srcOffsetBytes,
                                                            pinnedResult.AddrOfPinnedObject() + dstOffsetBytes, rowSizeBytes);
                                        dstOffsetBytes += (int)rowSizeBytes;
                                        srcOffsetBytes += (int)outRowStrideBytes;
                                    }
                                }
                            }

                            return(result);
                        }
                        finally {
                            InteropUtils.CallNative(
                                NativeMethods.ResourceFactory_UnmapSubresource,
                                RenderingModule.DeviceContext,
                                ResourceHandle,
                                GetSubresourceIndex(mipIndex)
                                ).ThrowOnFailure();
                        }
                    }
                    finally {
                        pinnedResult.Free();
                    }
                }
            });

            return(new TexelArray3D <TTexel>(data, MipWidth(mipIndex), MipHeight(mipIndex)));
        }
Ejemplo n.º 9
0
        /// <summary>
        /// Performs a <see cref="ResourceUsage.StagingRead"/> on this texture, copying all the data from every mip level and
        /// concatenating it in to a single <typeparamref name="TTexel"/> array.
        /// </summary>
        /// <returns>An array of all texels in this resource, ordered first by ascending mip level, then by slice, then row.</returns>
        /// <exception cref="ResourceOperationUnavailableException">Thrown if <see cref="BaseResource.CanRead"/> is
        /// <c>false</c>.</exception>
        public override TTexel[] ReadAll()
        {
            ThrowIfCannotRead();
            return(LosgapSystem.InvokeOnMaster(() => {
                TTexel[] result = new TTexel[SizeTexels];

                lock (InstanceMutationLock) {
                    if (IsDisposed)
                    {
                        Logger.Warn("Attempted read manipulation on disposed resource of type: " + GetType().Name);
                        return result;
                    }
                    GCHandle pinnedResult = GCHandle.Alloc(result, GCHandleType.Pinned);
                    try {
                        int dstOffsetBytes = 0;
                        for (uint i = 0; i < NumMips; ++i)
                        {
                            IntPtr outDataPtr;
                            uint outRowStrideBytes, outSliceStrideBytes;
                            InteropUtils.CallNative(
                                NativeMethods.ResourceFactory_MapSubresource,
                                RenderingModule.DeviceContext,
                                ResourceHandle,
                                GetSubresourceIndex(i),
                                ResourceMapping.Read,
                                (IntPtr)(&outDataPtr),
                                (IntPtr)(&outRowStrideBytes),
                                (IntPtr)(&outSliceStrideBytes)
                                ).ThrowOnFailure();

                            try {
                                uint numRows = MipHeight(i);
                                uint numSlices = MipDepth(i);
                                uint rowSizeBytes = MipWidth(i) * TexelSizeBytes;
                                uint sliceSizeBytes = MipHeight(i) * rowSizeBytes;
                                if (sliceSizeBytes == outSliceStrideBytes && rowSizeBytes == outRowStrideBytes)                                   // If we can copy the whole lot in one go, do it
                                {
                                    UnsafeUtils.MemCopy(outDataPtr,
                                                        pinnedResult.AddrOfPinnedObject() + dstOffsetBytes, sliceSizeBytes * numSlices);
                                    dstOffsetBytes += (int)(sliceSizeBytes * numSlices);
                                }
                                else                                   // Otherwise, copy slice-by-slice and row-by-row
                                {
                                    int srcOffsetBytes = 0;
                                    for (uint s = 0; s < numSlices; ++s)
                                    {
                                        int dstOffsetAdditionalBytes = 0;
                                        for (uint r = 0; r < numRows; ++r)
                                        {
                                            UnsafeUtils.MemCopy(outDataPtr + srcOffsetBytes,
                                                                pinnedResult.AddrOfPinnedObject() + dstOffsetBytes + dstOffsetAdditionalBytes, rowSizeBytes);
                                            dstOffsetAdditionalBytes += (int)rowSizeBytes;
                                            srcOffsetBytes += (int)outRowStrideBytes;
                                        }
                                        dstOffsetBytes += (int)sliceSizeBytes;
                                    }
                                }
                            }
                            finally {
                                InteropUtils.CallNative(
                                    NativeMethods.ResourceFactory_UnmapSubresource,
                                    RenderingModule.DeviceContext,
                                    ResourceHandle,
                                    GetSubresourceIndex(i)
                                    ).ThrowOnFailure();
                            }
                        }
                    }
                    finally {
                        pinnedResult.Free();
                    }
                }

                return result;
            }));
        }
Ejemplo n.º 10
0
        /// <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();
                }
            }
        }
Ejemplo n.º 11
0
        public void DiscardWrite(ArraySlice <TTexel> data, uint mipIndex = 0U,
                                 uint writeOffsetX = 0U, uint writeOffsetY = 0U)
        {
            Assure.LessThan(
                mipIndex, NumMips,
                "Can not write to mip level " + mipIndex + ": Only " + NumMips + " present in texture."
                );
            Assure.LessThanOrEqualTo(
                data.Length + writeOffsetX + (writeOffsetY * Width),
                Width * Height,
                "Buffer overrun: Please ensure you are not attempting to write past the end of the texture."
                );

            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;
                    if (srcRowStrideBytes == outRowStrideBytes)                       // If the data is aligned, just fast copy
                    {
                        UnsafeUtils.CopyGenericArray(
                            data,
                            outDataPtr + (int)((writeOffsetX + writeOffsetY * Width) * TexelSizeBytes),
                            TexelSizeBytes
                            );
                    }
                    else                       // Otherwise, copy row-by-row
                    {
                        GCHandle pinnedDataHandle = GCHandle.Alloc(data.ContainingArray, GCHandleType.Pinned);
                        try {
                            IntPtr srcDataPtr        = pinnedDataHandle.AddrOfPinnedObject();
                            uint   firstFullRowIndex = writeOffsetY + 1;                                                     // Inclusive
                            uint   lastFullRowIndex  = firstFullRowIndex + ((data.Length - (Width - writeOffsetX)) / Width); // Exclusive

                            // Write first row
                            UnsafeUtils.MemCopy(srcDataPtr,
                                                outDataPtr + (int)(((firstFullRowIndex - 1) * outRowStrideBytes) + (writeOffsetX * TexelSizeBytes)), (Width - writeOffsetX) * TexelSizeBytes);

                            int srcOffsetAtRowStart = (int)((Width - writeOffsetX) * TexelSizeBytes);
                            int dstOffsetAtRowStart = (int)(firstFullRowIndex * outRowStrideBytes);
                            for (uint r = firstFullRowIndex; r < lastFullRowIndex; ++r)
                            {
                                UnsafeUtils.MemCopy(srcDataPtr + srcOffsetAtRowStart,
                                                    outDataPtr + dstOffsetAtRowStart, srcRowStrideBytes);
                                srcOffsetAtRowStart += (int)srcRowStrideBytes;
                                dstOffsetAtRowStart += (int)outRowStrideBytes;
                            }

                            // Write last row
                            UnsafeUtils.MemCopy(srcDataPtr + srcOffsetAtRowStart,
                                                outDataPtr + dstOffsetAtRowStart, ((data.Length - (Width - writeOffsetX)) % Width) * TexelSizeBytes);
                        }
                        finally {
                            pinnedDataHandle.Free();
                        }
                    }
                }
                finally {
                    InteropUtils.CallNative(
                        NativeMethods.ResourceFactory_UnmapSubresource,
                        RenderingModule.DeviceContext,
                        ResourceHandle,
                        GetSubresourceIndex(mipIndex)
                        ).ThrowOnFailure();
                }
            }
        }