Repository: microsoft/terminal
File: src/renderer/atlas/AtlasEngine.cpp (C++)

Code before:
        _api.bufferLine.reserve(projectedTextSize);
        _api.bufferLineColumn.reserve(projectedTextSize + 1);
        _api.bufferLineMetadata = Buffer<BufferLineMetadata>{ _api.cellCount.x };
        _api.analysisResults = {};

        _api.clusterMap = Buffer<u16>{ projectedTextSize };
        _api.textProps = Buffer<DWRITE_SHAPING_TEXT_PROPERTIES>{ projectedTextSize };
        _api.glyphIndices = Buffer<u16>{ projectedGlyphSize };
        _api.glyphProps = Buffer<DWRITE_SHAPING_GLYPH_PROPERTIES>{ projectedGlyphSize };
        _api.glyphAdvances = Buffer<f32>{ projectedGlyphSize };
        _api.glyphOffsets = Buffer<DWRITE_GLYPH_OFFSET>{ projectedGlyphSize };

        // Initialize cellGlyphMapping with valid data (whitespace), so that it can be
        // safely used by the TileHashMap refresh logic via makeNewest() in StartPaint().
        {
            u16x2* coords;
            AtlasKey key{ { .cellCount = 1 }, 1, L" " };
            AtlasValue value{ CellFlags::None, 1, &coords };

            coords[0] = _r.tileAllocator.allocate(_r.glyphs);

            const auto it = _r.glyphs.insert(std::move(key), std::move(value));
            _r.glyphQueue.emplace_back(it);

            std::ranges::fill(_r.cellGlyphMapping, it);
        }
    }

    if (!_r.d2dMode)
    {
        // The RenderTargetView is later used with OMSetRenderTargets
        // to tell D3D where stuff is supposed to be rendered at.
        {
            wil::com_ptr<ID3D11Texture2D> buffer;
            THROW_IF_FAILED(_r.swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), buffer.put_void()));
            THROW_IF_FAILED(_r.device->CreateRenderTargetView(buffer.get(), nullptr, _r.renderTargetView.put()));
        }
        if (_r.customPixelShader)
        {
            D3D11_TEXTURE2D_DESC desc{};
            desc.Width = _api.sizeInPixel.x;
            desc.Height = _api.sizeInPixel.y;
            desc.MipLevels = 1;
            desc.ArraySize = 1;
            desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
            desc.SampleDesc = { 1, 0 };
            desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
            THROW_IF_FAILED(_r.device->CreateTexture2D(&desc, nullptr, _r.customOffscreenTexture.addressof()));
            THROW_IF_FAILED(_r.device->CreateShaderResourceView(_r.customOffscreenTexture.get(), nullptr, _r.customOffscreenTextureView.addressof()));
            THROW_IF_FAILED(_r.device->CreateRenderTargetView(_r.customOffscreenTexture.get(), nullptr, _r.customOffscreenTextureTargetView.addressof()));
        }

Review comment (bug):
The crash occurs when the viewport size changes causing the recreation of the `cellGlyphMapping` array and the first paint includes non-single-width line rendition rows. In that case the `cellGlyphMapping` array will have items that are still default-constructed pointers (null pointers in debug, uninitialized data in release mode), leading to a crash.

(I had to submit this comment again, because I moved this new code slightly.)

Code after:
        _api.bufferLine.reserve(projectedTextSize);
        _api.bufferLineColumn.reserve(projectedTextSize + 1);
        _api.bufferLineMetadata = Buffer<BufferLineMetadata>{ _api.cellCount.x };
        _api.analysisResults = {};

        _api.clusterMap = Buffer<u16>{ projectedTextSize };
        _api.textProps = Buffer<DWRITE_SHAPING_TEXT_PROPERTIES>{ projectedTextSize };
        _api.glyphIndices = Buffer<u16>{ projectedGlyphSize };
        _api.glyphProps = Buffer<DWRITE_SHAPING_GLYPH_PROPERTIES>{ projectedGlyphSize };
        _api.glyphAdvances = Buffer<f32>{ projectedGlyphSize };
        _api.glyphOffsets = Buffer<DWRITE_GLYPH_OFFSET>{ projectedGlyphSize };

        // Initialize cellGlyphMapping with valid data (whitespace), so that it can be
        // safely used by the TileHashMap refresh logic via makeNewest() in StartPaint().
        {
            u16x2* coords{};
            AtlasKey key{ { .cellCount = 1 }, 1, L" " };
            AtlasValue value{ CellFlags::None, 1, &coords };

            coords[0] = _r.tileAllocator.allocate(_r.glyphs);

            const auto it = _r.glyphs.insert(std::move(key), std::move(value));
            _r.glyphQueue.emplace_back(it);

            std::fill(_r.cellGlyphMapping.begin(), _r.cellGlyphMapping.end(), it);
        }
    }

    if (!_r.d2dMode)
    {
        // The RenderTargetView is later used with OMSetRenderTargets
        // to tell D3D where stuff is supposed to be rendered at.
        {
            wil::com_ptr<ID3D11Texture2D> buffer;
            THROW_IF_FAILED(_r.swapChain->GetBuffer(0, __uuidof(ID3D11Texture2D), buffer.put_void()));
            THROW_IF_FAILED(_r.device->CreateRenderTargetView(buffer.get(), nullptr, _r.renderTargetView.put()));
        }
        if (_r.customPixelShader)
        {
            D3D11_TEXTURE2D_DESC desc{};
            desc.Width = _api.sizeInPixel.x;
            desc.Height = _api.sizeInPixel.y;
            desc.MipLevels = 1;
            desc.ArraySize = 1;
            desc.Format = DXGI_FORMAT_B8G8R8A8_UNORM;
            desc.SampleDesc = { 1, 0 };
            desc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET;
            THROW_IF_FAILED(_r.device->CreateTexture2D(&desc, nullptr, _r.customOffscreenTexture.addressof()));
            THROW_IF_FAILED(_r.device->CreateShaderResourceView(_r.customOffscreenTexture.get(), nullptr, _r.customOffscreenTextureView.addressof()));
            THROW_IF_FAILED(_r.device->CreateRenderTargetView(_r.customOffscreenTexture.get(), nullptr, _r.customOffscreenTextureTargetView.addressof()));
        }