19#include <freerdp/config.h>
22#include <winpr/assert.h>
24#include <freerdp/log.h>
27#include "shadow_encoder.h"
28#include "shadow_rfx_dirty.h"
30#define TAG SERVER_TAG("shadow.rfx_dirty")
32#define RFX_TILE_SIZE 64ul
35#define RFX_BYTES_PER_PIXEL 4ul
39static UINT64 sFrames = 0;
40static UINT64 sTilesSeen = 0;
41static UINT64 sTilesSent = 0;
42static UINT64 sEmptyFrames = 0;
43static UINT64 sWireBytes = 0;
45void shadow_rfx_invalidate_snapshot(rdpShadowEncoder* encoder)
47 WINPR_ASSERT(encoder);
49 free(encoder->gfxSnapshot);
50 encoder->gfxSnapshot =
nullptr;
51 free(encoder->gfxTileEncoded);
52 encoder->gfxTileEncoded =
nullptr;
53 encoder->gfxSnapshotWidth = 0;
54 encoder->gfxSnapshotHeight = 0;
55 encoder->gfxSnapshotStep = 0;
56 encoder->gfxSnapshotValid = FALSE;
59static BOOL shadow_rfx_snapshot_ensure(rdpShadowEncoder* encoder, UINT16 width, UINT16 height,
62 WINPR_ASSERT(encoder);
64 if (encoder->gfxSnapshotValid && (encoder->gfxSnapshotWidth == width) &&
65 (encoder->gfxSnapshotHeight == height) && (encoder->gfxSnapshotStep == step))
68 shadow_rfx_invalidate_snapshot(encoder);
70 if (width == 0 || height == 0 || step == 0)
72 WLog_WARN(TAG,
"invalid snapshot geometry (%" PRIu16
"x%" PRIu16
" step=%" PRIu32
")",
76 if (step > SIZE_MAX / height)
78 WLog_WARN(TAG,
"snapshot size overflows SIZE_MAX (%" PRIu32
"x%" PRIu16
")", step, height);
82 const UINT32 gridW = (width + RFX_TILE_SIZE - 1) / RFX_TILE_SIZE;
83 const UINT32 gridH = (height + RFX_TILE_SIZE - 1) / RFX_TILE_SIZE;
85 encoder->gfxSnapshot = calloc(height, step);
86 encoder->gfxTileEncoded = calloc(gridW, gridH *
sizeof(BOOL));
88 if (!encoder->gfxSnapshot || !encoder->gfxTileEncoded)
90 WLog_WARN(TAG,
"snapshot alloc failed (%ux%u step=%" PRIu32
")", width, height, step);
91 shadow_rfx_invalidate_snapshot(encoder);
95 encoder->gfxSnapshotWidth = width;
96 encoder->gfxSnapshotHeight = height;
97 encoder->gfxSnapshotStep = step;
98 encoder->gfxSnapshotValid = TRUE;
102int shadow_rfx_plan_tiles(rdpShadowEncoder* encoder,
const BYTE* pSrcData, UINT32 nSrcStep,
104 RFX_RECT** outRects, UINT16* outNumRects)
106 WINPR_ASSERT(encoder);
107 WINPR_ASSERT(pSrcData);
109 WINPR_ASSERT(outRects);
110 WINPR_ASSERT(outNumRects);
111 WINPR_ASSERT(nSrcStep > 0);
116 UINT32 x1 = cmd->right;
117 UINT32 y1 = cmd->bottom;
124 if ((cmd->left >= x1) || (cmd->top >= y1))
127 const UINT32 gridW = (srcWidth + RFX_TILE_SIZE - 1) / RFX_TILE_SIZE;
128 const UINT32 gridH = (srcHeight + RFX_TILE_SIZE - 1) / RFX_TILE_SIZE;
129 const UINT32 startTx = cmd->left / RFX_TILE_SIZE;
130 const UINT32 endTx = (x1 - 1) / RFX_TILE_SIZE;
131 const UINT32 startTy = cmd->top / RFX_TILE_SIZE;
132 const UINT32 endTy = (y1 - 1) / RFX_TILE_SIZE;
138 const BOOL snapOk = shadow_rfx_snapshot_ensure(encoder, srcWidth, srcHeight, nSrcStep);
142 BOOL* bandChanged = calloc(gridW,
sizeof(BOOL));
149 for (UINT32 ty = startTy; ty <= endTy; ty++)
151 const UINT32 tileY = ty * RFX_TILE_SIZE;
152 UINT32 tileH = RFX_TILE_SIZE;
153 if ((tileY + RFX_TILE_SIZE) > srcHeight)
154 tileH = srcHeight - tileY;
156 memset(bandChanged, 0, gridW *
sizeof(BOOL));
163 BOOL bandDirty = FALSE;
167 for (UINT32 r = 0; r < tileH; r++)
170 ((size_t)(tileY + r)) * nSrcStep + ((
size_t)cmd->left * RFX_BYTES_PER_PIXEL);
171 const UINT32 spanBytes = (x1 - cmd->left) * RFX_BYTES_PER_PIXEL;
172 if (memcmp(&pSrcData[ofs], &encoder->gfxSnapshot[ofs], spanBytes) != 0)
180 for (UINT32 tx = startTx; tx <= endTx; tx++)
182 const UINT32 tileX = tx * RFX_TILE_SIZE;
183 UINT32 tileW = RFX_TILE_SIZE;
184 if ((tileX + RFX_TILE_SIZE) > srcWidth)
185 tileW = srcWidth - tileX;
189 if (snapOk && encoder->gfxTileEncoded[ty * gridW + tx])
194 for (UINT32 r = 0; r < tileH; r++)
196 const size_t ofs = ((size_t)(tileY + r)) * nSrcStep +
197 ((
size_t)tileX * RFX_BYTES_PER_PIXEL);
198 if (memcmp(&pSrcData[ofs], &encoder->gfxSnapshot[ofs],
199 (
size_t)tileW * RFX_BYTES_PER_PIXEL) != 0)
214 rects[numRects].x = WINPR_ASSERTING_INT_CAST(UINT16, tileX);
215 rects[numRects].y = WINPR_ASSERTING_INT_CAST(UINT16, tileY);
216 rects[numRects].width = WINPR_ASSERTING_INT_CAST(UINT16, tileW);
217 rects[numRects].height = WINPR_ASSERTING_INT_CAST(UINT16, tileH);
225 sTilesSeen += (UINT64)(endTx - startTx + 1) * (endTy - startTy + 1);
230 *outNumRects = numRects;
234void shadow_rfx_commit_tiles(rdpShadowEncoder* encoder,
const BYTE* pSrcData, UINT32 nSrcStep,
235 const RFX_RECT* rects, UINT16 numRects, UINT32 wireBytes)
237 WINPR_ASSERT(encoder);
238 WINPR_ASSERT(pSrcData);
239 WINPR_ASSERT(rects || (numRects == 0));
240 WINPR_ASSERT(nSrcStep > 0);
242 if (!encoder->gfxSnapshotValid)
245 const UINT32 gridW = (encoder->gfxSnapshotWidth + RFX_TILE_SIZE - 1) / RFX_TILE_SIZE;
246 const UINT32 gridH = (encoder->gfxSnapshotHeight + RFX_TILE_SIZE - 1) / RFX_TILE_SIZE;
248 for (UINT16 i = 0; i < numRects; i++)
250 const UINT32 tileX = rects[i].x;
251 const UINT32 tileY = rects[i].y;
252 const UINT32 tx = tileX / RFX_TILE_SIZE;
253 const UINT32 ty = tileY / RFX_TILE_SIZE;
255 if ((tx < gridW) && (ty < gridH))
257 for (UINT32 r = 0; r < rects[i].height; r++)
260 ((size_t)(tileY + r)) * nSrcStep + ((
size_t)tileX * RFX_BYTES_PER_PIXEL);
261 CopyMemory(&encoder->gfxSnapshot[ofs], &pSrcData[ofs],
262 rects[i].width * RFX_BYTES_PER_PIXEL);
265 encoder->gfxTileEncoded[ty * gridW + tx] = TRUE;
270 sTilesSent += numRects;
271 sWireBytes += wireBytes;
273 if ((sFrames % 120) == 0)
276 "stats: frames=%" PRIu64
" tiles_seen=%" PRIu64
" tiles_sent=%" PRIu64
277 " tiles_skipped=%" PRIu64
" empty_frames=%" PRIu64
" wire_bytes=%" PRIu64,
278 sFrames, sTilesSeen, sTilesSent, sTilesSeen - sTilesSent, sEmptyFrames,