IFACEMETHODIMP CanvasSpriteBatch::Close() { return ExceptionBoundary([&] { auto deviceContext = m_deviceContext.Close(); if (!deviceContext) return; if (m_sprites.empty()) // early out if there's nothing to draw return; // // Sort the sprites // if (m_sortMode == CanvasSpriteSortMode::Bitmap) { std::stable_sort(m_sprites.begin(), m_sprites.end(), [] (auto const& a, auto const& b) { return a.Bitmap.Get() < b.Bitmap.Get(); }); } // // Build up a D2D sprite batch from our sprites // ComPtr<ID2D1SpriteBatch> spriteBatch; ThrowIfFailed(deviceContext->CreateSpriteBatch(&spriteBatch)); assert(m_sprites.size() < std::numeric_limits<uint32_t>::max()); auto firstSprite = &m_sprites.front(); auto stride = static_cast<uint32_t>(sizeof(Sprite)); ThrowIfFailed(spriteBatch->AddSprites( static_cast<uint32_t>(m_sprites.size()), &firstSprite->DestinationRect, &firstSprite->SourceRect, &firstSprite->Color, &firstSprite->Transform, stride, stride, stride, stride)); // // Get the device context into the right state // auto originalAntialiasMode = deviceContext->GetAntialiasMode(); if (originalAntialiasMode == D2D1_ANTIALIAS_MODE_PER_PRIMITIVE) deviceContext->SetAntialiasMode(D2D1_ANTIALIAS_MODE_ALIASED); auto originalUnitMode = deviceContext->GetUnitMode(); if (originalUnitMode != m_unitMode) deviceContext->SetUnitMode(m_unitMode); // // Draw the sprites - one DrawSpriteBatch call for each bitmap // // Figure out if we need to quirk the batch size to workaround an issue // with older Qualcomm drivers. ComPtr<ID2D1Device> d2dDevice; deviceContext->GetDevice(&d2dDevice); auto device = ResourceManager::GetOrCreate<ICanvasDeviceInternal>(d2dDevice.Get()); bool quirked = device->IsSpriteBatchQuirkRequired(); uint32_t maxSpritesPerBatch = quirked ? 256 : std::numeric_limits<uint32_t>::max(); for (BatchFinder<Sprite> batchFinder(m_sprites, maxSpritesPerBatch); !batchFinder.Done(); batchFinder.FindNext()) { deviceContext->DrawSpriteBatch( spriteBatch.Get(), batchFinder.CurrentStartIndex(), batchFinder.CurrentSpriteCount(), batchFinder.CurrentBitmap(), m_interpolationMode, m_spriteOptions); if (quirked) { // Direct2D will helpfully batch up our DrawSpriteBatch calls - when // we're manually unbatching them to avoid limits of the maximum sprites per batch! // An explicit Flush here prevents that from happening. deviceContext->Flush(); } } // // Restore the state we may have changed // if (originalUnitMode != m_unitMode) deviceContext->SetUnitMode(originalUnitMode); if (originalAntialiasMode == D2D1_ANTIALIAS_MODE_PER_PRIMITIVE) deviceContext->SetAntialiasMode(originalAntialiasMode); }); }