1//
2// Copyright (c) 2012 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7// Image11.h: Implements the rx::Image11 class, which acts as the interface to
8// the actual underlying resources of a Texture
9
10#include "libGLESv2/renderer/d3d/d3d11/Renderer11.h"
11#include "libGLESv2/renderer/d3d/d3d11/Image11.h"
12#include "libGLESv2/renderer/d3d/d3d11/TextureStorage11.h"
13#include "libGLESv2/renderer/d3d/d3d11/formatutils11.h"
14#include "libGLESv2/renderer/d3d/d3d11/renderer11_utils.h"
15#include "libGLESv2/Framebuffer.h"
16#include "libGLESv2/FramebufferAttachment.h"
17#include "libGLESv2/main.h"
18
19#include "common/utilities.h"
20
21namespace rx
22{
23
24Image11::Image11()
25{
26    mStagingTexture = NULL;
27    mRenderer = NULL;
28    mDXGIFormat = DXGI_FORMAT_UNKNOWN;
29    mRecoverFromStorage = false;
30    mAssociatedStorage = NULL;
31    mAssociatedStorageLevel = 0;
32    mAssociatedStorageLayerTarget = 0;
33    mRecoveredFromStorageCount = 0;
34}
35
36Image11::~Image11()
37{
38    disassociateStorage();
39    releaseStagingTexture();
40}
41
42Image11 *Image11::makeImage11(Image *img)
43{
44    ASSERT(HAS_DYNAMIC_TYPE(rx::Image11*, img));
45    return static_cast<rx::Image11*>(img);
46}
47
48void Image11::generateMipmap(Image11 *dest, Image11 *src)
49{
50    ASSERT(src->getDXGIFormat() == dest->getDXGIFormat());
51    ASSERT(src->getWidth() == 1 || src->getWidth() / 2 == dest->getWidth());
52    ASSERT(src->getHeight() == 1 || src->getHeight() / 2 == dest->getHeight());
53
54    const d3d11::DXGIFormat &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(src->getDXGIFormat());
55    ASSERT(dxgiFormatInfo.mipGenerationFunction != NULL);
56
57    D3D11_MAPPED_SUBRESOURCE destMapped;
58    HRESULT destMapResult = dest->map(D3D11_MAP_WRITE, &destMapped);
59    if (FAILED(destMapResult))
60    {
61        ERR("Failed to map destination image for mip map generation. HRESULT:0x%X", destMapResult);
62        return;
63    }
64
65    D3D11_MAPPED_SUBRESOURCE srcMapped;
66    HRESULT srcMapResult = src->map(D3D11_MAP_READ, &srcMapped);
67    if (FAILED(srcMapResult))
68    {
69        ERR("Failed to map source image for mip map generation. HRESULT:0x%X", srcMapResult);
70
71        dest->unmap();
72        return;
73    }
74
75    const uint8_t *sourceData = reinterpret_cast<const uint8_t*>(srcMapped.pData);
76    uint8_t *destData = reinterpret_cast<uint8_t*>(destMapped.pData);
77
78    dxgiFormatInfo.mipGenerationFunction(src->getWidth(), src->getHeight(), src->getDepth(),
79                                         sourceData, srcMapped.RowPitch, srcMapped.DepthPitch,
80                                         destData, destMapped.RowPitch, destMapped.DepthPitch);
81
82    dest->unmap();
83    src->unmap();
84
85    dest->markDirty();
86}
87
88bool Image11::isDirty() const
89{
90    // If mDirty is true
91    // AND mStagingTexture doesn't exist AND mStagingTexture doesn't need to be recovered from TextureStorage
92    // AND the texture doesn't require init data (i.e. a blank new texture will suffice)
93    // then isDirty should still return false.
94    if (mDirty && !mStagingTexture && !mRecoverFromStorage && !(d3d11::GetTextureFormatInfo(mInternalFormat).dataInitializerFunction != NULL))
95    {
96        return false;
97    }
98
99    return mDirty;
100}
101
102bool Image11::copyToStorage2D(TextureStorage *storage, int level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
103{
104    TextureStorage11_2D *storage11 = TextureStorage11_2D::makeTextureStorage11_2D(storage);
105    return copyToStorageImpl(storage11, level, 0, xoffset, yoffset, width, height);
106}
107
108bool Image11::copyToStorageCube(TextureStorage *storage, int face, int level, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
109{
110    TextureStorage11_Cube *storage11 = TextureStorage11_Cube::makeTextureStorage11_Cube(storage);
111    return copyToStorageImpl(storage11, level, face, xoffset, yoffset, width, height);
112}
113
114bool Image11::copyToStorage3D(TextureStorage *storage, int level, GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth)
115{
116    TextureStorage11_3D *storage11 = TextureStorage11_3D::makeTextureStorage11_3D(storage);
117    return copyToStorageImpl(storage11, level, 0, xoffset, yoffset, width, height);
118}
119
120bool Image11::copyToStorage2DArray(TextureStorage *storage, int level, GLint xoffset, GLint yoffset, GLint arrayLayer, GLsizei width, GLsizei height)
121{
122    TextureStorage11_2DArray *storage11 = TextureStorage11_2DArray::makeTextureStorage11_2DArray(storage);
123    return copyToStorageImpl(storage11, level, arrayLayer, xoffset, yoffset, width, height);
124}
125
126bool Image11::copyToStorageImpl(TextureStorage11 *storage11, int level, int layerTarget, GLint xoffset, GLint yoffset, GLsizei width, GLsizei height)
127{
128    // If an app's behavior results in an Image11 copying its data to/from to a TextureStorage multiple times,
129    // then we should just keep the staging texture around to prevent the copying from impacting perf.
130    // We allow the Image11 to copy its data to/from TextureStorage once.
131    // This accounts for an app making a late call to glGenerateMipmap.
132    bool attemptToReleaseStagingTexture = (mRecoveredFromStorageCount < 2);
133
134    if (attemptToReleaseStagingTexture)
135    {
136        // If another image is relying on this Storage for its data, then we must let it recover its data before we overwrite it.
137        storage11->releaseAssociatedImage(level, layerTarget, this);
138    }
139
140    bool updateSubresourceSuccess = storage11->updateSubresourceLevel(getStagingTexture(), getStagingSubresource(), level, layerTarget, xoffset, yoffset, 0, width, height, 1);
141
142    // Once the image data has been copied into the Storage, we can release it locally.
143    if (attemptToReleaseStagingTexture && updateSubresourceSuccess)
144    {
145        storage11->associateImage(this, level, layerTarget);
146        releaseStagingTexture();
147        mRecoverFromStorage = true;
148        mAssociatedStorage = storage11;
149        mAssociatedStorageLevel = level;
150        mAssociatedStorageLayerTarget = layerTarget;
151    }
152
153    return updateSubresourceSuccess;
154}
155
156bool Image11::isAssociatedStorageValid(TextureStorage11* textureStorage) const
157{
158    return (mAssociatedStorage == textureStorage);
159}
160
161bool Image11::recoverFromAssociatedStorage()
162{
163    if (mRecoverFromStorage)
164    {
165        createStagingTexture();
166
167        bool textureStorageCorrect = mAssociatedStorage->isAssociatedImageValid(mAssociatedStorageLevel, mAssociatedStorageLayerTarget, this);
168
169        // This means that the cached TextureStorage has been modified after this Image11 released its copy of its data.
170        // This should not have happened. The TextureStorage should have told this Image11 to recover its data before it was overwritten.
171        ASSERT(textureStorageCorrect);
172
173        if (textureStorageCorrect)
174        {
175            // CopySubResource from the Storage to the Staging texture
176            mAssociatedStorage->copySubresourceLevel(mStagingTexture, mStagingSubresource, mAssociatedStorageLevel, mAssociatedStorageLayerTarget, 0, 0, 0, mWidth, mHeight, mDepth);
177            mRecoveredFromStorageCount += 1;
178        }
179
180        // Reset all the recovery parameters, even if the texture storage association is broken.
181        disassociateStorage();
182
183        return textureStorageCorrect;
184    }
185
186    return false;
187}
188
189void Image11::disassociateStorage()
190{
191    if (mRecoverFromStorage)
192    {
193        // Make the texturestorage release the Image11 too
194        mAssociatedStorage->disassociateImage(mAssociatedStorageLevel, mAssociatedStorageLayerTarget, this);
195
196        mRecoverFromStorage = false;
197        mAssociatedStorage = NULL;
198        mAssociatedStorageLevel = 0;
199        mAssociatedStorageLayerTarget = 0;
200    }
201}
202
203bool Image11::redefine(Renderer *renderer, GLenum target, GLenum internalformat, GLsizei width, GLsizei height, GLsizei depth, bool forceRelease)
204{
205    if (mWidth != width ||
206        mHeight != height ||
207        mInternalFormat != internalformat ||
208        forceRelease)
209    {
210        // End the association with the TextureStorage, since that data will be out of date.
211        // Also reset mRecoveredFromStorageCount since this Image is getting completely redefined.
212        disassociateStorage();
213        mRecoveredFromStorageCount = 0;
214
215        mRenderer = Renderer11::makeRenderer11(renderer);
216
217        mWidth = width;
218        mHeight = height;
219        mDepth = depth;
220        mInternalFormat = internalformat;
221        mTarget = target;
222
223        // compute the d3d format that will be used
224        const d3d11::TextureFormat &formatInfo = d3d11::GetTextureFormatInfo(internalformat);
225        const d3d11::DXGIFormat &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(formatInfo.texFormat);
226        mDXGIFormat = formatInfo.texFormat;
227        mActualFormat = dxgiFormatInfo.internalFormat;
228        mRenderable = (formatInfo.rtvFormat != DXGI_FORMAT_UNKNOWN);
229
230        SafeRelease(mStagingTexture);
231        mDirty = (formatInfo.dataInitializerFunction != NULL);
232
233        return true;
234    }
235
236    return false;
237}
238
239DXGI_FORMAT Image11::getDXGIFormat() const
240{
241    // this should only happen if the image hasn't been redefined first
242    // which would be a bug by the caller
243    ASSERT(mDXGIFormat != DXGI_FORMAT_UNKNOWN);
244
245    return mDXGIFormat;
246}
247
248// Store the pixel rectangle designated by xoffset,yoffset,width,height with pixels stored as format/type at input
249// into the target pixel rectangle.
250void Image11::loadData(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
251                       GLint unpackAlignment, GLenum type, const void *input)
252{
253    const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(mInternalFormat);
254    GLsizei inputRowPitch = formatInfo.computeRowPitch(type, width, unpackAlignment);
255    GLsizei inputDepthPitch = formatInfo.computeDepthPitch(type, width, height, unpackAlignment);
256
257    const d3d11::DXGIFormat &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(mDXGIFormat);
258    GLuint outputPixelSize = dxgiFormatInfo.pixelBytes;
259
260    const d3d11::TextureFormat &d3dFormatInfo = d3d11::GetTextureFormatInfo(mInternalFormat);
261    LoadImageFunction loadFunction = d3dFormatInfo.loadFunctions.at(type);
262
263    D3D11_MAPPED_SUBRESOURCE mappedImage;
264    HRESULT result = map(D3D11_MAP_WRITE, &mappedImage);
265    if (FAILED(result))
266    {
267        ERR("Could not map image for loading.");
268        return;
269    }
270
271    uint8_t* offsetMappedData = (reinterpret_cast<uint8_t*>(mappedImage.pData) + (yoffset * mappedImage.RowPitch + xoffset * outputPixelSize + zoffset * mappedImage.DepthPitch));
272    loadFunction(width, height, depth,
273                 reinterpret_cast<const uint8_t*>(input), inputRowPitch, inputDepthPitch,
274                 offsetMappedData, mappedImage.RowPitch, mappedImage.DepthPitch);
275
276    unmap();
277}
278
279void Image11::loadCompressedData(GLint xoffset, GLint yoffset, GLint zoffset, GLsizei width, GLsizei height, GLsizei depth,
280                                 const void *input)
281{
282    const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(mInternalFormat);
283    GLsizei inputRowPitch = formatInfo.computeRowPitch(GL_UNSIGNED_BYTE, width, 1);
284    GLsizei inputDepthPitch = formatInfo.computeDepthPitch(GL_UNSIGNED_BYTE, width, height, 1);
285
286    const d3d11::DXGIFormat &dxgiFormatInfo = d3d11::GetDXGIFormatInfo(mDXGIFormat);
287    GLuint outputPixelSize = dxgiFormatInfo.pixelBytes;
288    GLuint outputBlockWidth = dxgiFormatInfo.blockWidth;
289    GLuint outputBlockHeight = dxgiFormatInfo.blockHeight;
290
291    ASSERT(xoffset % outputBlockWidth == 0);
292    ASSERT(yoffset % outputBlockHeight == 0);
293
294    const d3d11::TextureFormat &d3dFormatInfo = d3d11::GetTextureFormatInfo(mInternalFormat);
295    LoadImageFunction loadFunction = d3dFormatInfo.loadFunctions.at(GL_UNSIGNED_BYTE);
296
297    D3D11_MAPPED_SUBRESOURCE mappedImage;
298    HRESULT result = map(D3D11_MAP_WRITE, &mappedImage);
299    if (FAILED(result))
300    {
301        ERR("Could not map image for loading.");
302        return;
303    }
304
305    uint8_t* offsetMappedData = reinterpret_cast<uint8_t*>(mappedImage.pData) + ((yoffset / outputBlockHeight) * mappedImage.RowPitch +
306                                                                           (xoffset / outputBlockWidth) * outputPixelSize +
307                                                                           zoffset * mappedImage.DepthPitch);
308
309    loadFunction(width, height, depth,
310                 reinterpret_cast<const uint8_t*>(input), inputRowPitch, inputDepthPitch,
311                 offsetMappedData, mappedImage.RowPitch, mappedImage.DepthPitch);
312
313    unmap();
314}
315
316void Image11::copy(GLint xoffset, GLint yoffset, GLint zoffset, GLint x, GLint y, GLsizei width, GLsizei height, gl::Framebuffer *source)
317{
318    gl::FramebufferAttachment *colorbuffer = source->getReadColorbuffer();
319
320    if (colorbuffer && colorbuffer->getActualFormat() == mActualFormat)
321    {
322        // No conversion needed-- use copyback fastpath
323        ID3D11Texture2D *colorBufferTexture = NULL;
324        unsigned int subresourceIndex = 0;
325
326        if (mRenderer->getRenderTargetResource(colorbuffer, &subresourceIndex, &colorBufferTexture))
327        {
328            D3D11_TEXTURE2D_DESC textureDesc;
329            colorBufferTexture->GetDesc(&textureDesc);
330
331            ID3D11Device *device = mRenderer->getDevice();
332            ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();
333
334            ID3D11Texture2D* srcTex = NULL;
335            if (textureDesc.SampleDesc.Count > 1)
336            {
337                D3D11_TEXTURE2D_DESC resolveDesc;
338                resolveDesc.Width = textureDesc.Width;
339                resolveDesc.Height = textureDesc.Height;
340                resolveDesc.MipLevels = 1;
341                resolveDesc.ArraySize = 1;
342                resolveDesc.Format = textureDesc.Format;
343                resolveDesc.SampleDesc.Count = 1;
344                resolveDesc.SampleDesc.Quality = 0;
345                resolveDesc.Usage = D3D11_USAGE_DEFAULT;
346                resolveDesc.BindFlags = 0;
347                resolveDesc.CPUAccessFlags = 0;
348                resolveDesc.MiscFlags = 0;
349
350                HRESULT result = device->CreateTexture2D(&resolveDesc, NULL, &srcTex);
351                if (FAILED(result))
352                {
353                    ERR("Failed to create resolve texture for Image11::copy, HRESULT: 0x%X.", result);
354                    return;
355                }
356
357                deviceContext->ResolveSubresource(srcTex, 0, colorBufferTexture, subresourceIndex, textureDesc.Format);
358                subresourceIndex = 0;
359            }
360            else
361            {
362                srcTex = colorBufferTexture;
363                srcTex->AddRef();
364            }
365
366            D3D11_BOX srcBox;
367            srcBox.left = x;
368            srcBox.right = x + width;
369            srcBox.top = y;
370            srcBox.bottom = y + height;
371            srcBox.front = 0;
372            srcBox.back = 1;
373
374            deviceContext->CopySubresourceRegion(mStagingTexture, 0, xoffset, yoffset, zoffset, srcTex, subresourceIndex, &srcBox);
375
376            SafeRelease(srcTex);
377            SafeRelease(colorBufferTexture);
378        }
379    }
380    else
381    {
382        // This format requires conversion, so we must copy the texture to staging and manually convert via readPixels
383        D3D11_MAPPED_SUBRESOURCE mappedImage;
384        HRESULT result = map(D3D11_MAP_WRITE, &mappedImage);
385        if (FAILED(result))
386        {
387            ERR("Failed to map texture for Image11::copy, HRESULT: 0x%X.", result);
388            return;
389        }
390
391        // determine the offset coordinate into the destination buffer
392        GLsizei rowOffset = gl::GetInternalFormatInfo(mActualFormat).pixelBytes * xoffset;
393        uint8_t *dataOffset = static_cast<uint8_t*>(mappedImage.pData) + mappedImage.RowPitch * yoffset + rowOffset + zoffset * mappedImage.DepthPitch;
394
395        const gl::InternalFormat &formatInfo = gl::GetInternalFormatInfo(mInternalFormat);
396
397        mRenderer->readPixels(source, x, y, width, height, formatInfo.format, formatInfo.type, mappedImage.RowPitch, gl::PixelPackState(), dataOffset);
398
399        unmap();
400    }
401}
402
403ID3D11Resource *Image11::getStagingTexture()
404{
405    createStagingTexture();
406
407    return mStagingTexture;
408}
409
410void Image11::releaseStagingTexture()
411{
412    SafeRelease(mStagingTexture);
413}
414
415unsigned int Image11::getStagingSubresource()
416{
417    createStagingTexture();
418
419    return mStagingSubresource;
420}
421
422void Image11::createStagingTexture()
423{
424    if (mStagingTexture)
425    {
426        return;
427    }
428
429    const DXGI_FORMAT dxgiFormat = getDXGIFormat();
430
431    if (mWidth > 0 && mHeight > 0 && mDepth > 0)
432    {
433        ID3D11Device *device = mRenderer->getDevice();
434        HRESULT result;
435
436        int lodOffset = 1;
437        GLsizei width = mWidth;
438        GLsizei height = mHeight;
439
440        // adjust size if needed for compressed textures
441        d3d11::MakeValidSize(false, dxgiFormat, &width, &height, &lodOffset);
442
443        if (mTarget == GL_TEXTURE_3D)
444        {
445            ID3D11Texture3D *newTexture = NULL;
446
447            D3D11_TEXTURE3D_DESC desc;
448            desc.Width = width;
449            desc.Height = height;
450            desc.Depth = mDepth;
451            desc.MipLevels = lodOffset + 1;
452            desc.Format = dxgiFormat;
453            desc.Usage = D3D11_USAGE_STAGING;
454            desc.BindFlags = 0;
455            desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
456            desc.MiscFlags = 0;
457
458            if (d3d11::GetTextureFormatInfo(mInternalFormat).dataInitializerFunction != NULL)
459            {
460                std::vector<D3D11_SUBRESOURCE_DATA> initialData;
461                std::vector< std::vector<BYTE> > textureData;
462                d3d11::GenerateInitialTextureData(mInternalFormat, width, height, mDepth,
463                                                  lodOffset + 1, &initialData, &textureData);
464
465                result = device->CreateTexture3D(&desc, initialData.data(), &newTexture);
466            }
467            else
468            {
469                result = device->CreateTexture3D(&desc, NULL, &newTexture);
470            }
471
472            if (FAILED(result))
473            {
474                ASSERT(result == E_OUTOFMEMORY);
475                ERR("Creating image failed.");
476                return gl::error(GL_OUT_OF_MEMORY);
477            }
478
479            mStagingTexture = newTexture;
480            mStagingSubresource = D3D11CalcSubresource(lodOffset, 0, lodOffset + 1);
481        }
482        else if (mTarget == GL_TEXTURE_2D || mTarget == GL_TEXTURE_2D_ARRAY || mTarget == GL_TEXTURE_CUBE_MAP)
483        {
484            ID3D11Texture2D *newTexture = NULL;
485
486            D3D11_TEXTURE2D_DESC desc;
487            desc.Width = width;
488            desc.Height = height;
489            desc.MipLevels = lodOffset + 1;
490            desc.ArraySize = 1;
491            desc.Format = dxgiFormat;
492            desc.SampleDesc.Count = 1;
493            desc.SampleDesc.Quality = 0;
494            desc.Usage = D3D11_USAGE_STAGING;
495            desc.BindFlags = 0;
496            desc.CPUAccessFlags = D3D11_CPU_ACCESS_READ | D3D11_CPU_ACCESS_WRITE;
497            desc.MiscFlags = 0;
498
499            if (d3d11::GetTextureFormatInfo(mInternalFormat).dataInitializerFunction != NULL)
500            {
501                std::vector<D3D11_SUBRESOURCE_DATA> initialData;
502                std::vector< std::vector<BYTE> > textureData;
503                d3d11::GenerateInitialTextureData(mInternalFormat, width, height, 1,
504                                                  lodOffset + 1, &initialData, &textureData);
505
506                result = device->CreateTexture2D(&desc, initialData.data(), &newTexture);
507            }
508            else
509            {
510                result = device->CreateTexture2D(&desc, NULL, &newTexture);
511            }
512
513            if (FAILED(result))
514            {
515                ASSERT(result == E_OUTOFMEMORY);
516                ERR("Creating image failed.");
517                return gl::error(GL_OUT_OF_MEMORY);
518            }
519
520            mStagingTexture = newTexture;
521            mStagingSubresource = D3D11CalcSubresource(lodOffset, 0, lodOffset + 1);
522        }
523        else
524        {
525            UNREACHABLE();
526        }
527    }
528
529    mDirty = false;
530}
531
532HRESULT Image11::map(D3D11_MAP mapType, D3D11_MAPPED_SUBRESOURCE *map)
533{
534    createStagingTexture();
535
536    // We must recover from the TextureStorage if necessary, even for D3D11_MAP_WRITE.
537    recoverFromAssociatedStorage();
538
539    HRESULT result = E_FAIL;
540
541    if (mStagingTexture)
542    {
543        ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();
544        result = deviceContext->Map(mStagingTexture, mStagingSubresource, mapType, 0, map);
545
546        // this can fail if the device is removed (from TDR)
547        if (d3d11::isDeviceLostError(result))
548        {
549            mRenderer->notifyDeviceLost();
550        }
551        else if (SUCCEEDED(result))
552        {
553            mDirty = true;
554        }
555    }
556
557    return result;
558}
559
560void Image11::unmap()
561{
562    if (mStagingTexture)
563    {
564        ID3D11DeviceContext *deviceContext = mRenderer->getDeviceContext();
565        deviceContext->Unmap(mStagingTexture, mStagingSubresource);
566    }
567}
568
569}
570