gl::Error Framebuffer9::readPixelsImpl(const gl::Rectangle &area, GLenum format, GLenum type, size_t outputPitch, const gl::PixelPackState &pack, uint8_t *pixels) const { ASSERT(pack.pixelBuffer.get() == nullptr); const gl::FramebufferAttachment *colorbuffer = mState.getColorAttachment(0); ASSERT(colorbuffer); RenderTarget9 *renderTarget = nullptr; gl::Error error = colorbuffer->getRenderTarget(&renderTarget); if (error.isError()) { return error; } ASSERT(renderTarget); IDirect3DSurface9 *surface = renderTarget->getSurface(); ASSERT(surface); D3DSURFACE_DESC desc; surface->GetDesc(&desc); if (desc.MultiSampleType != D3DMULTISAMPLE_NONE) { UNIMPLEMENTED(); // FIXME: Requires resolve using StretchRect into non-multisampled render target SafeRelease(surface); return gl::Error(GL_OUT_OF_MEMORY, "ReadPixels is unimplemented for multisampled framebuffer attachments."); } IDirect3DDevice9 *device = mRenderer->getDevice(); ASSERT(device); HRESULT result; IDirect3DSurface9 *systemSurface = nullptr; bool directToPixels = !pack.reverseRowOrder && pack.alignment <= 4 && mRenderer->getShareHandleSupport() && area.x == 0 && area.y == 0 && static_cast<UINT>(area.width) == desc.Width && static_cast<UINT>(area.height) == desc.Height && desc.Format == D3DFMT_A8R8G8B8 && format == GL_BGRA_EXT && type == GL_UNSIGNED_BYTE; if (directToPixels) { // Use the pixels ptr as a shared handle to write directly into client's memory result = device->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &systemSurface, reinterpret_cast<void**>(&pixels)); if (FAILED(result)) { // Try again without the shared handle directToPixels = false; } } if (!directToPixels) { result = device->CreateOffscreenPlainSurface(desc.Width, desc.Height, desc.Format, D3DPOOL_SYSTEMMEM, &systemSurface, nullptr); if (FAILED(result)) { ASSERT(result == D3DERR_OUTOFVIDEOMEMORY || result == E_OUTOFMEMORY); SafeRelease(surface); return gl::Error(GL_OUT_OF_MEMORY, "Failed to allocate internal texture for ReadPixels."); } } result = device->GetRenderTargetData(surface, systemSurface); SafeRelease(surface); if (FAILED(result)) { SafeRelease(systemSurface); // It turns out that D3D will sometimes produce more error // codes than those documented. if (d3d9::isDeviceLostError(result)) { mRenderer->notifyDeviceLost(); } else { UNREACHABLE(); } return gl::Error(GL_OUT_OF_MEMORY, "Failed to read internal render target data."); } if (directToPixels) { SafeRelease(systemSurface); return gl::Error(GL_NO_ERROR); } RECT rect; rect.left = gl::clamp(area.x, 0L, static_cast<LONG>(desc.Width)); rect.top = gl::clamp(area.y, 0L, static_cast<LONG>(desc.Height)); rect.right = gl::clamp(area.x + area.width, 0L, static_cast<LONG>(desc.Width)); rect.bottom = gl::clamp(area.y + area.height, 0L, static_cast<LONG>(desc.Height)); D3DLOCKED_RECT lock; result = systemSurface->LockRect(&lock, &rect, D3DLOCK_READONLY); if (FAILED(result)) { UNREACHABLE(); SafeRelease(systemSurface); return gl::Error(GL_OUT_OF_MEMORY, "Failed to lock internal render target."); } uint8_t *source; int inputPitch; if (pack.reverseRowOrder) { source = reinterpret_cast<uint8_t*>(lock.pBits) + lock.Pitch * (rect.bottom - rect.top - 1); inputPitch = -lock.Pitch; } else { source = reinterpret_cast<uint8_t*>(lock.pBits); inputPitch = lock.Pitch; } const d3d9::D3DFormat &d3dFormatInfo = d3d9::GetD3DFormatInfo(desc.Format); const gl::InternalFormat &sourceFormatInfo = gl::GetInternalFormatInfo(d3dFormatInfo.internalFormat); if (sourceFormatInfo.format == format && sourceFormatInfo.type == type) { // Direct copy possible for (int y = 0; y < rect.bottom - rect.top; y++) { memcpy(pixels + y * outputPitch, source + y * inputPitch, (rect.right - rect.left) * sourceFormatInfo.pixelBytes); } } else { const d3d9::D3DFormat &sourceD3DFormatInfo = d3d9::GetD3DFormatInfo(desc.Format); ColorCopyFunction fastCopyFunc = sourceD3DFormatInfo.getFastCopyFunction(format, type); GLenum sizedDestInternalFormat = gl::GetSizedInternalFormat(format, type); const gl::InternalFormat &destFormatInfo = gl::GetInternalFormatInfo(sizedDestInternalFormat); if (fastCopyFunc) { // Fast copy is possible through some special function for (int y = 0; y < rect.bottom - rect.top; y++) { for (int x = 0; x < rect.right - rect.left; x++) { uint8_t *dest = pixels + y * outputPitch + x * destFormatInfo.pixelBytes; const uint8_t *src = source + y * inputPitch + x * sourceFormatInfo.pixelBytes; fastCopyFunc(src, dest); } } } else { ColorReadFunction colorReadFunction = sourceD3DFormatInfo.colorReadFunction; ColorWriteFunction colorWriteFunction = GetColorWriteFunction(format, type); uint8_t temp[sizeof(gl::ColorF)]; for (int y = 0; y < rect.bottom - rect.top; y++) { for (int x = 0; x < rect.right - rect.left; x++) { uint8_t *dest = pixels + y * outputPitch + x * destFormatInfo.pixelBytes; const uint8_t *src = source + y * inputPitch + x * sourceFormatInfo.pixelBytes; // readFunc and writeFunc will be using the same type of color, CopyTexImage // will not allow the copy otherwise. colorReadFunction(src, temp); colorWriteFunction(temp, dest); } } } } systemSurface->UnlockRect(); SafeRelease(systemSurface); return gl::Error(GL_NO_ERROR); }
void PackPixels(const PackPixelsParams ¶ms, const angle::Format &sourceFormat, int inputPitchIn, const uint8_t *sourceIn, uint8_t *destWithoutOffset) { uint8_t *destWithOffset = destWithoutOffset + params.offset; const uint8_t *source = sourceIn; int inputPitch = inputPitchIn; if (params.pack.reverseRowOrder) { source += inputPitch * (params.area.height - 1); inputPitch = -inputPitch; } const auto &sourceGLInfo = gl::GetSizedInternalFormatInfo(sourceFormat.glInternalFormat); if (sourceGLInfo.format == params.format && sourceGLInfo.type == params.type) { // Direct copy possible for (int y = 0; y < params.area.height; ++y) { memcpy(destWithOffset + y * params.outputPitch, source + y * inputPitch, params.area.width * sourceGLInfo.pixelBytes); } return; } ASSERT(sourceGLInfo.sized); gl::FormatType formatType(params.format, params.type); ColorCopyFunction fastCopyFunc = GetFastCopyFunction(sourceFormat.fastCopyFunctions, formatType); const auto &destFormatInfo = gl::GetInternalFormatInfo(formatType.format, formatType.type); if (fastCopyFunc) { // Fast copy is possible through some special function for (int y = 0; y < params.area.height; ++y) { for (int x = 0; x < params.area.width; ++x) { uint8_t *dest = destWithOffset + y * params.outputPitch + x * destFormatInfo.pixelBytes; const uint8_t *src = source + y * inputPitch + x * sourceGLInfo.pixelBytes; fastCopyFunc(src, dest); } } return; } ColorWriteFunction colorWriteFunction = GetColorWriteFunction(formatType); // Maximum size of any Color<T> type used. uint8_t temp[16]; static_assert(sizeof(temp) >= sizeof(gl::ColorF) && sizeof(temp) >= sizeof(gl::ColorUI) && sizeof(temp) >= sizeof(gl::ColorI), "Unexpected size of gl::Color struct."); const auto &colorReadFunction = sourceFormat.colorReadFunction; for (int y = 0; y < params.area.height; ++y) { for (int x = 0; x < params.area.width; ++x) { uint8_t *dest = destWithOffset + y * params.outputPitch + x * destFormatInfo.pixelBytes; const uint8_t *src = source + y * inputPitch + x * sourceGLInfo.pixelBytes; // readFunc and writeFunc will be using the same type of color, CopyTexImage // will not allow the copy otherwise. colorReadFunction(src, temp); colorWriteFunction(temp, dest); } } }