mirror of
https://github.com/libretro/RetroArch.git
synced 2026-11-07 08:36:02 -10:00
* video: per-screen geometry for the headset's floating screens Each floating screen, by the core's logical screen id, gets its own offsets, yaw, pitch, roll, scale and visibility (video_screen_xr_t), applied on top of where the layout puts it. video_views_xr_reset gives the defaults and video_views_xr_normalize clamps each value to its range: offsets to 10 m either way, angles to 180 degrees, scale from 0.1 to 10. A planner given no screens uses the defaults, so nothing moves until a setting does. * configuration: save per-screen headset geometry video_screen<N>_xr_x, _y, _z, _yaw, _pitch, _roll, _scale and _visible keep each floating screen's adjustments for screen ids 1 to 8, and survive configuration overrides like the other video settings. A minimal save writes only the values that differ from the defaults. The video_screens settings sample checks the round trip and the override cases, and runs in the OpenXR input and Wine views jobs. The defaults fixture now links video_views.c, which the reset uses. * menu: per-screen controls for the headset's floating screens Stereo 3D & Screens gains Screen, which picks a floating screen by its logical id among those in the current frame's map, and for it Horizontal, Vertical and Depth Offset, Yaw, Pitch, Roll, Scale and Visible, with Reset Screen and Reset All Screens. The rows apply live and are hidden where they cannot apply, such as a tracked projection's eyes. Stepping between screens reads one snapshot of the map and is bounded, so a tracking change mid-step cannot leave it walking an empty map. docs/openxr.md describes the controls; the video_screens sample drives them through the shipping menu actions. * video: apply per-screen headset geometry to the frame snapshots Each XR planner, Vulkan's and GL's, takes the normalized screen settings with the frame snapshot, so a screen moves, turns, scales or hides as its rows change, while paused too; recentering and tracked projections are unaffected. Screen images are kept between frames, so shader feedback survives a placement change. A retained XR target that cannot be allocated is skipped quietly, remembered by its size so it is not retried every frame; only the desktop UI and canvas targets log the failure. Covered by openxr_core_test, the video_screens sample, the e2e runner's feedback shader case and the new vulkan_views_target sample. * android: keep the OpenXR EGL context C89 gl_xr_egl_init declared its extension string after statements. * video: pose inverse, layout spots and grab maths for headset edit mode Each item's layout spot comes from one helper that placement uses and the quad set records per slot, so a shown pose turns back into the settings that placed it. Grab maths: the item follows the aim, level unless tilting, offsets clamped to the settings' range; the stick pushes along the ray and resizes. Edit picking sees every item and takes the menu by a handle bar under it. * video: a frame without a view map is screen 1 in the headset A core without a views map (SNES, say) had no screen to select, so its frame could not be moved. Its whole frame now takes screen 1's offsets, turn, scale and visibility, and the selector lists Screen 1. gl's window stays as laid out while the menu is open over it. * video: the headset's menu in front of screen 1, or where it was put The menu sat at a fixed spot, so moving screen 1 closer left the menu behind it. It now floats 10 cm in front of screen 1, facing as it does, at its default angular size and never nearer than 0.4 m, while screen 1 floats and shows, and is still submitted after the screens. A menu place (video_openxr_menu_xr_* with video_openxr_menu_placed) overrides that; it is saved like the per-screen keys, and Reset All Screens clears it. * input: Edit Headset Screens mode for the OpenXR controllers Edit Screens starts from a hotkey, a headset action, the HEADSET_EDIT_SCREENS command or a Stereo 3D & Screens row, and stops on the same, on opening the menu, on a video reinit or when the session ends. It syncs its own action set in place of the game's, points the lasers at every item whatever the Laser Pointer setting, keeps the menu's quad up while editing, and with While Editing Screens set to Pause pauses only content that was running. A runtime that refuses its bindings keeps the game's and leaves edit mode unavailable. The setting reaches the headset's poll with the others, and the pause and resume are asked of the frontend as platform requests, so the input driver issues no command and takes no settings itself. A Laser Pointer value past its choices, which a hand-edited config can hold, reads as Auto rather than as edit mode's. * input: grab, tilt, push and resize headset screens in edit mode A grip on an item takes it in the screens' frame: it keeps its pose relative to the hand, stays level unless the trigger is held, and the stick pushes it along the laser and resizes it, past a deadzone. Each poll writes the item's offsets, turn and scale where the menu's rows write them, through named frontend calls, clamped so it waits at a limit and resumes from there. The menu is taken by its handle; a menu that follows screen 1 is placed as large as it shows. The main button resets a free item as Reset Screen does, or puts the menu back in front of screen 1. A recenter, a screen leaving the map and leaving edit mode let go, counted for the frame snapshot; an untracked hand holds its item still. * openxr: held screens follow the hand at the headset's rate The main thread publishes what each hand holds through a seqlock; each headset frame re-places a held item, both eyes of a stereo screen, from that frame's own aim, so it no longer waits for the next video frame. An item let go stays where the hand left it until a quad set planned at or after its release (an edit count carried in the frame snapshot) shows it, even once the hand holds another item, so nothing steps back. Lazy Follow holds its glide while anything is held. Edit mode outlines every item, brightly the one under a laser, and draws the menu's handle bar under it, from the laser's image, now an atlas of the dot, two outlines and two handle bars. They take only the layers the screens leave after the dots. * openxr: edit mode end-to-end cases and documentation The test layer's aim can roll, and the e2e runner drives edit mode against Monado: grabs that follow the hand on every headset frame and stay where they are let go, level and tilt, the stick, resets of a screen and of the menu, the menu taken by its handle and following Screen 1 at its size, outlines and the handle, a no-map core, a views core's fallback frame, Follow Head, Lazy Follow, recentering, two hands, pausing, and what starts and ends edit mode, including the headset's hotkey bits with no command interface. The cases whose work is a host's or driver's own run on glcore and gl too. docs/openxr.md explains editing screens in the headset and where the menu goes. * openxr: a laser beam from each controller to its dot A thin strip runs from each hand's aim to its dot, or 1.5 m into nothing, turned about its own axis each headset frame to face the head so it never shows edge on. It comes from the laser's image, so it needs no new swapchain, and uses layers left after the dots and edit mode's outlines, so it is the first to go when the runtime has few. The new Laser Beam setting shows it in edit mode only (the default), always, or never; outside edit mode, Laser Pointer and its hotkey hide it with the dot. * openxr: edit mode outlines in three bands, outside the item A light band between two dark ones shows on white, black and busy borders alike. The rim now sits wholly outside its item, 5 mm clear of it even after filtering, so it never covers the picture; its region grows to keep two texels a band at about the old thickness. Screens' outlines are submitted before the screens, so a neighbour placed flush covers the part of a rim over its picture, while the menu's outline and handle bar stay on top. The handle bar moves 5 mm below the menu's outline, which it would otherwise cover, and gets the same bands, faint and bright. * input: choose a headset edit item with Select Next and Previous Edit mode steps from the laser through the screens it can move and the menu. A chosen item is the one lit, either hand's grip takes it from wherever the hand is, and Reset resets it, so a screen hidden behind another can be moved. Whatever a hand holds stays lit. A ray on the menu's handle bar takes the menu before any screen in front of it. Within 3 cm of the bar, a ray that came from the bar keeps the menu even over a screen, and one that came from elsewhere takes it only where no screen is hit. * openxr: snap a held headset screen to the screens near it Snap to Screens, on by default, turns a held screen parallel or square to a nearby screen and lines up its plane, edges or centre. Each pull is exact within 3 cm or 5 degrees and fades out over as far again, so the screen catches up with the hand rather than jumping, and snapping fades in over the first 5 cm or 10 degrees after a grab, so taking a screen never moves it. With several screens near, the snap that leaves the least error wins, so a near screen's exact pull beats a far one's fading pull whatever their order. One stateless function inside the grab serves the main thread and the headset's thread, so a screen is saved where it was shown. The holding hand buzzes as a snap takes hold, once, and not again until the screen has left that pull. * input: turn a held headset item with the free hand's stick A quarter turn of a held screen needed the controller turned a quarter turn, which swung the screen round the hand on its long reach, so a square snap could not be reached. Now the stick of the hand holding nothing turns what the other hand holds about the vertical through its middle, faster the further it is pushed, and snapping still catches it parallel or square. A carried item keeps its pitch and roll and its angle to the way the hand points across the floor, so raising, lowering or twisting the hand no longer tips or swivels it; the trigger lets it ride the hand rigidly. * openxr: smooth the controller aim in edit mode A screen held about 2 m out on the laser magnified every tremor of the hand. In edit mode each hand's aim now goes through a One Euro filter, which smooths a slow hand hard and a fast one little, with its rate taken from the headset frame's own time step. The headset's thread filters once a frame and hands the result to the main thread, which grabs, moves and lets go from it, so a screen is saved where it was shown. The new Edit Smoothing setting picks Off, Low, Medium (the default) or High; outside edit mode the lasers are never smoothed. * openxr: name the chosen edit item beside the controller Edit mode now shows a faint tag just above the hand that last pressed Select Next or Previous, reading Laser, Screen N or Menu, so what the grip will take is plain without a notice. The words are drawn once into the laser's image in the default font and the interface language, light over a dark halo like the outlines so they read on any background, and the tag is one more headset layer; when the runtime is short of layers the beams are dropped before it. On a runtime whose swapchains are too narrow for the words, the laser's image keeps its old width and there is no tag. * input: tilt a held headset item with the free hand's stick In edit mode the free hand's stick already turns the item the other hand holds. Its up and down now tilt that item too, about the item's own left-right line through its middle, at the turn's rate and with no limit: up tips the top back, as when pushing a monitor's top back. It works whether the item is carried or rides the hand with the trigger held, and snaps as the turn does. Within about 25 degrees of left or right the stick only turns, and within about 25 degrees of up or down it only tilts, so a push meant for one does not drift into the other. Edit mode's starting notice lists the turn and tilt and stays about 8 seconds. * input: snap a turned or tilted headset item to angle steps While the free hand's stick turns or tilts a held item in edit mode, the item clicks into steps of the new Snap to Angles setting (15 degrees by default; 30, 45, 90 or Off), so it is easy to set straight. The turn counts from facing the user's head across the floor, the tilt from upright. Within about 2 degrees of a step the item holds there and the holding hand buzzes once; the stick's own angle keeps adding up, so pushing on leaves the step. Only the stick steps: carrying an item or tilting it with the trigger never clicks, and a snap to another screen that would engage wins over a step. * openxr: show the back of a headset screen turned away Runtimes draw only the front of a quad layer, so a screen or the menu turned away from the head vanished. When the head is behind a quad's plane, the frame now submits a plain dark panel in that quad's place: a solid grey cell of the laser's image, at the quad's size, turned to face the head. It takes the quad's own layer, so the layer count and cap are unchanged. Edit mode picks either face, so an item turned away stays grabbable, and its outline, handle and dot turn with its back. * input: keep a screen snap's angle once the free stick lets go A snap to another screen shows a held screen square or parallel to it, but the item's own angle stays where the stick or the hand took it, up to several degrees off. The next turn with the free hand's stick started from that hidden angle, so the screen left the snap early on one side, late on the other, and caught up with a lurch. Now, as the free stick returns to its deadzone and as a push starts, a snap engaged on the turn becomes the item's own angle: the carried turn, or the orientation the trigger tilts. A snap still fading in never does, since that would hop the screen into it. The place is left to the hand, and carrying alone never keeps the angle, so pointing still pulls a screen out of a snap. Letting go of the grip leaves the item where it shows, as before. * openxr: hinge a held headset screen to a neighbour's edge Snap to Screens now also hinges a held screen to another along a shared edge at any angle between them, so a DS or 3DS pair can be opened at a slant: an edge within 5 degrees of parallel to one of the other's, within 3 cm of its line and sharing some length with it, turns exactly parallel and moves onto it, centres or ends level, its angle left free. The pull fades in over the first 3 cm of shared length, so a screen slides off the end of the other's edge without a jump, and a diagonal neighbour never hinges. Screens that would overlap do not hinge; the pull is whole from flat to square and fades out folded shut. Flat and square snap as before and report a hinge at 0 and 90. A turn to the same screen pivots on the shared edge, so the edge stays on the line as the screen leaves flat or square. The snap still keeps nothing between calls; its result carries a hinge bit, and video_xr_snap_hinge also says which edge and line, for the stick to swing about. * input: swing a hinged headset screen about the shared edge While a held screen is hinged to another, the free hand's stick axis that turns nearest the shared edge, the tilt for a top or bottom edge and the turn for a side one, now swings it about that edge, so it stays attached; the other axis still turns or tilts it and can pull it off. Where the swing leaves it, angle and place, it stays as the stick lets go. Snap to Angles counts a hinged screen's steps from flat with the other screen. A hinge counts only while the screen's own edge is within its range, not when other snaps alone hold it on the line, so a swing never carries a screen round a line it is far from. A square corner is a side hinge at 90 degrees, so the turn stick there now swings the screen about the corner's edge. Main thread only; nothing new reaches the frame loop, which still shows the grab through video_xr_grab_pose. * openxr: leave a gap between hinged headset screens Edges that meet in edit mode, side by side, at a corner or hinged at any angle, now click to a set gap from within 1 cm and otherwise keep the gap the hand gives them, still hinged, up to 10 cm (letting go by 13). Screens no longer have to touch, which made them overlap. Each grab carries the gap, so the edit poll and the frame loop snap alike; until a setting sets it, it is 0. Pushed past touching, a hinged edge stops there for the first centimetre of the push, the stop fading out by 3 cm, and ticks once per push. A swing pivots on the held screen's own edge, so the gap keeps through it, and a commit keeps the hand's gap unless the click holds it. After a swing lets go past square, that stick axis and its steps keep pivoting on the same edge until the edge leaves the hinge's reach, so a flick past square and back does not creep. Of two neighbours within the 5 degree turn snap, the nearer edge wins the turn and the hinge, by one measure for every screen, so a screen stacked behind, one off the ends of the edge or a side wall at a corner never outranks a hinge the turn holds. * menu: add Hinge Gap for headset screens Hinge Gap (0, 1, 2, 3, 5 or 10 cm, default 2) sets the gap edit mode's Snap to Screens leaves between screens that meet edge to edge, side by side, at a corner or hinged. Edit mode takes it as it starts and gives it to each grab; the click to it buzzes like the other snaps. * menu: put Edit Screens under Headset Output Stereo 3D & Screens now lists Edit Screens and its snaps right under the switch that reveals them, then the headset's own setup, then where the screens go, with the per-screen rows last and Visible first among them. * openxr: track each held screen's hinge on the frame loop The frame loop already re-snaps each held screen every frame. It now also keeps, per hand, the hinge that snap holds whole, until the edge leaves the hinge's 4.5 cm reach, where the frame draws it, so edit mode can mark it. A kept hinge whose target no longer matches is dropped. video_xr gains the hinge line's ends, along the overlap of the two real edges and midway across the gap, an outline cut that drops one edge's rim, and a green strip in the laser's image, before the tags. * openxr: mark a hinged headset screen with a green line While a held screen's hinge holds, the two edges that meet drop their outline rim and a 6 mm green line runs along the hinge where they overlap, midway across the gap, so it is clear when the hinge has taken hold. The line is drawn as the beam is, a centimetre nearer the eye than the edges, since SteamVR does not keep the order of overlapping quad layers, and comes after the outlines in the layer budget; hidden rims cost no layer. * input: buzz longer as a headset screen hinges A hinge taking hold now gets one 150 ms buzz at 0.8 on the hand carrying the screen, against the 20 ms snap tick, so the attach can be felt. It fires again only after the edge leaves the hinge's reach, hinges elsewhere, or the other hand takes the screen it hinges on, so a swing past square and back does not repeat it. Ticks due while it plays are skipped rather than cutting it short. * openxr: find headset screens resting in a hinge Two screens no hand holds rest in a hinge when their poses pass the test that engages a carried screen's hinge, with either one carried, and the hinge, held whole with nothing else pulling, leaves the carried one where it is. So a hinge found at rest stays hinged when its screen is taken. The engaged test moves into its own function so both use the same one, and one target can pull whole whatever the grab's fade. * openxr: keep a headset screen's hinge shown at rest In Edit Screens, two screens no hand holds that rest in a hinge keep the green line and their hidden rims, whether let go hinged or loaded that way. The main thread counts them only when a resting screen changes and sends them with the hands' record, so a release and its resting pair arrive together; the frame loop places each line from the screens it shows. A screen hinged at rest on two adjacent edges still keeps one of those rims. The hands' record now outgrows the quads, so the seqlock's copy is sized to the larger of the two. * openxr: pick up a hinged headset screen still hinged Taking up a screen that rests in a hinge starts the carry on that hinge, whole from the first poll, with no buzz and no jump, until it leaves the hinge's reach, another hinge takes hold, or the other hand takes the screen it hinges on. Until the grab's own snapping has faded in, the kept hinge's target still pulls whole, so a straight pull out does not hop. The grab carries the hinge so both threads hold it the same; of several, the one the snap ranks best carries over, and only where holding it moves nothing. While the other screens fade in, the hinge's screen also leads the turn, so a nearer one still fading does not take it. * openxr: end a headset screen's hinge where a push passes the stop Pushing a hinged screen into its neighbour holds the edge at touching for 3 cm of push, fading out by 5.5 cm. Where it stops holding, the hinge ends: the line goes, the rims come back, and the lid and buzz are cleared, so screens pushed into each other show no hinge that a let-go there would not rest in. Pulled back, the hinge takes hold again 5 mm short of where it ended, with the long buzz, so a tremor there does not flicker it. The grab carries that through flag, and the frame loop keeps its line until the main thread sets it, so the two threads never disagree for more than a poll. At Hinge Gap 1 a pair let go touching rests hinged and is taken up hinged, as at every other gap. A screen swung into a square corner gets the long buzz on arrival, and the other hand taking the target does not hop the carried screen by the push's depth. * input: let go of a headset screen where it was before the grip opened Opening the grip twitches the hand, and the poll that sees the grip open would move the screen by that twitched aim. Half a degree of pitch moves a screen 1.8 m out by 1.6 cm, enough to knock it off its hinge's gap or out of level, so it would not rest hinged. Each hand keeps a short trail of where, turned and as large as its screen showed, entries at least 10 ms apart, and a grip let go puts the screen as it showed 80 ms before (INPUT_OPENXR_EDIT_REWIND_US), or as it was taken if held for less. Edit mode's end and a recenter let go where the screen shows. * openxr: find where a let-go headset screen settles in its hinge A screen let go while its hinge shows should land exactly in that hinge, so the hinge still shows at rest and a pick-up is hinged. video_xr_hinge_settle holds the hinge whole from the hand's pose, then moves the edge to the set gap from within 3 cm of it (touching stays where the stop holds it) and levels it along the edge within the level pull's reach. Its result must pass the rest test, and it turns no further than a grab's hinge engages, which shares that test. A pair resting inside a settle band but off its point is no longer listed at rest, since a pick-up and let-go would move it. A touching rest is taken 5 mm deeper into the push stop, at every Hinge Gap, so a hand that drifts less than that apart before letting go settles touching rather than at the gap. * openxr: settle a let-go headset screen into its hinge A screen let go while its hinge shows now goes exactly into that hinge: to the set gap from within 3 cm of it, touching from the stop's hold, and level along the edge from within the level pull's reach. It then rests there, listed and green, in the same publish, and is saved there. Elsewhere it stays where it shows, as before. Each trail entry also records where the hand put the screen with no snap and whether a let-go there settles. At the let-go the rewound entry is settled again against the screens as they are now, at that entry's size, and put in one go. * openxr: let a hinged headset screen rest at any fold Snap to Screens' turn to flat, and its turn to square at a corner, is whole within 5 degrees and fades out by 10, so a hinged screen held there shows at twice its angle off less 10. No fold shown in that fade read as a rest: the pair was not listed, a let-go there did not settle into its hinge, and taking it up would have moved it. Where a hand takes a rest, it now holds the screen at the angle the turn's fade shows it from, the inverse of the turn's pull, so every fold rests, is listed and is taken up unmoved. Held snapping is unchanged. * input: keep a headset screen's hinge through a shaking hand Edit Screens' hinge buzz, lid and push stop followed the snap's own engage and reach on every poll. A hand shaking half a degree (1.6 cm at the screen) about a hinge's edge flipped them many times a second, each flip a long buzz. Each hold now keeps one mark: the hinge a let-go would settle into. It takes hold at once, with the long buzz, and lets go only once polls have seen the hand out of where a let-go would settle for 150 ms, each poll counting at most 50 ms, so a stalled main thread or a spell untracked never lets it go alone. The other hand taking its target, hinged elsewhere or not, the target leaving and the let-go end it at once. The lid waits for the mark to go, the push stop follows it, and a let-go while it shows settles into it from where the hand last would have, edit mode's end and a recenter included. * openxr: draw a held screen's hinge as the main thread marks it The grab carries the main thread's hinge mark, and the frame loop draws a held screen's green line and cuts its rims from that mark alone, through video_xr_grab_marked, while it snaps the screen every frame. A frame loop taking the hinge by itself between polls would flicker the line for a hand shaking at the edge of the hinge's range while the mark held. A mark whose target the frame does not show cuts no rim. The other hand taking the mark's target ends it, and the lid, on any poll, tracked or not, so a lost hand's line does not come back when that hand lets go. video_xr_grab_near_hinge loses its past argument, which only the frame loop's keep used. * openxr: keep edit mode's hinge layers in place SteamVR does not keep submission order for overlapping quad layers, so a layer added, dropped or moved to another index can pop the edit outlines in front of the screens, as a wobble at a hinge would do to its line's layer. The hinge lines are fixed slots: one for each hand holding a screen, hand 0 first, then one for each pair at rest. A slot with no line to show is drawn in place from its screen's middle, at rows of the laser image that are empty either way up. The layer count and every index change only at a grab, a let-go or a re-count of the pairs at rest; a mark only changes crops and which rows a slot shows. * input: date a let-go's trail by frame and stop it at a stall Each trail entry is dated by the frame its aims are from, one a frame and per hand, and the release poll records a new frame's aims first. The rewind stops at a stall, entries more than 2.5 frames apart, a frame counted as at least 20 ms so a fast headset's close entries never read as one. Under load a stall of 0.1 to 0.3 s can come just before the release; the entry from 80 ms back would then hold the pose from before it and step the let-go back across all the stall's motion. With Edit Smoothing on, which damps the twitch, a let-go that does not settle into a hinge stays where and as large as it shows: a rewind there would step back by what the smoothed aim still lags. The hinge decision and the settle use the trail either way. * openxr: cut edit outlines from cells instead of turning them Cutting a top or bottom rim from the one outline image turns both outline quads a quarter turn and swaps their width and height, and on SteamVR that can blink every edit outline as the hinge takes hold or lets go; side cuts, which never turn, do not. The laser's image carries twelve pre-cut outline cells after the tags, faint and bright, for a cut top, bottom, left, right, top and bottom, or left and right, so a cut only picks a cell and the quads keep their pose and size. The image is then 5582 texels wide. A runtime whose swapchains are narrower, or that does not say how wide they can be, keeps the turned and cropped outline at 2498 or 1208 wide. One log line at the laser's creation gives the width chosen and the runtime's limit. GL writes the top and bottom cells swapped for its bottom-up rows, and Android's copy of the image is allocated rather than static. The edit-hinge-3ds-narrow e2e case runs the turned cut at RA_XR_LAYER_MAX_WIDTH=4096, on Vulkan and the GL EGL lanes. * video: draw headset screens straight into the runtime's images Each headset screen was drawn into an image of the driver's own and copied from it into the runtime's on every new frame, Vulkan with a render pass and GL3 with a blit. Screens are now drawn into the runtime's image itself, as before per-screen placement, with no image of the driver's and no copy. A screen drawn again with no new frame - resized, made again or moved while paused - replays the last frame's final pass from the chain: build_viewport_pass_replay draws it at the new size from what it read then, the passes' outputs, their feedback and the history that frame saw, with that frame's count. Nothing else runs, feedback does not swap and history stays, so a feedback or history preset holds still while paused. A chain made to replay keeps one more history image, so the frame's end writes where the last frame did not read; the window's chains keep what they did. This also has movie frames reach the headset, which the copy held at the first one, and draws a screen resized while paused at its new size rather than stretching the old image. * openxr: leave what users who never edit see as it was Screens seen from behind turn to their grey backs only while Edit Screens is on, where screens are turned around; outside it nothing changes and no head is located for them. The grabs carry whether editing is on. The menu opens at its own spot, straight ahead of the anchor, as it did. Menu in Front of Screen 1, off by default, keeps it just in front of screen 1 and moving with it. The frame loop copies the main thread's grabs only once their stamp moves, so outside edit mode no frame copies them. A new session clears the frame loop's own copy and passes over what was published before it, rather than publishing itself: the main thread stays the grabs' one writer. On Touch, the left menu button that opens the menu from edit mode is spent until let go: held on as edit mode ends, it is no Start and does not toggle the menu again after a second. --------- Co-authored-by: libretroadmin <[email protected]>
3065 lines
156 KiB
YAML
3065 lines
156 KiB
YAML
name: CI Linux samples/gfx
|
|
|
|
on:
|
|
push:
|
|
branches:
|
|
- master
|
|
pull_request:
|
|
branches:
|
|
- master
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
env:
|
|
ACTIONS_ALLOW_USE_UNSECURE_NODE_VERSION: true
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
|
|
cancel-in-progress: ${{ github.ref != 'refs/heads/master' }}
|
|
|
|
jobs:
|
|
metal-frame-lease:
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
env:
|
|
APT_PKGS: clang libgnustep-base-dev libblocksruntime-dev ffmpeg
|
|
|
|
steps:
|
|
- uses: actions/checkout@v6
|
|
|
|
- name: Apt cache key
|
|
id: apt
|
|
run: sh .github/workflows/scripts/apt-cache-key.sh "$APT_PKGS"
|
|
|
|
- name: Restore apt cache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
restore-keys: ${{ steps.apt.outputs.prefix }}
|
|
|
|
- name: Install dependencies
|
|
timeout-minutes: 15
|
|
run: sh .github/workflows/scripts/apt-install.sh $APT_PKGS
|
|
|
|
- name: Save apt cache
|
|
if: env.APT_CACHE_SAVE == '1'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
|
|
- name: Strict C89 geometry with Clang
|
|
run: CC=clang python3 samples/gfx/metal_frame/run.py
|
|
- name: VideoToolbox HEVC parameter ownership and session setup
|
|
run: |
|
|
python3 samples/gfx/videotoolbox_hevc/codec_fixtures.py --verify
|
|
python3 samples/gfx/videotoolbox_hevc/hdr_codec_fixtures.py --verify
|
|
python3 samples/gfx/videotoolbox_hevc/sequence_fixtures.py --verify
|
|
CC=clang python3 samples/gfx/videotoolbox_hevc/run.py --apple-limits
|
|
python3 samples/gfx/videotoolbox_hevc/run.py
|
|
CFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer' python3 samples/gfx/videotoolbox_hevc/run.py
|
|
python3 samples/gfx/videotoolbox_hevc/run.py --threadless
|
|
CFLAGS='-DRETRO_ATOMIC_FORCE_VOLATILE -DRETRO_ATOMIC_SUPPRESS_WARNING' python3 samples/gfx/videotoolbox_hevc/run.py
|
|
TSAN_OPTIONS=halt_on_error=1 CFLAGS='-fsanitize=thread -fno-omit-frame-pointer' RUNNER="setarch $(uname -m) -R" python3 samples/gfx/videotoolbox_hevc/run.py
|
|
- name: VideoToolbox VP9 lifecycle and retained renderer snapshots
|
|
run: |
|
|
for compiler in gcc clang; do
|
|
CC="$compiler" make -C samples/gfx/videotoolbox_vp9 clean check SANITIZER=address,undefined
|
|
done
|
|
TSAN_OPTIONS=halt_on_error=1 CFLAGS='-fsanitize=thread -fno-omit-frame-pointer' RUNNER="setarch $(uname -m) -R" python3 samples/gfx/videotoolbox_vp9/run.py
|
|
|
|
- name: Native recording reinit ownership
|
|
run: |
|
|
make -C samples/record/gpu_reinit check CC=gcc SANITIZER=address,undefined
|
|
make -C samples/record/gpu_reinit clean
|
|
make -C samples/record/gpu_reinit check CC=clang SANITIZER=address,undefined
|
|
- name: HEVC software and hardware sequencing regression
|
|
working-directory: libretro-common/samples/formats/h265
|
|
run: |
|
|
make -j2 all SANITIZER=address,undefined
|
|
./rh265_malformed_test
|
|
./rh265_weight_test
|
|
./rh265_lossless_test
|
|
./rh265_lossless_test_nothreads
|
|
./rh265_lossless_test_volatile
|
|
- name: Retained Metal lease ownership and completion
|
|
run: python3 samples/gfx/metal_frame/run.py --mock
|
|
|
|
video-thread-state:
|
|
name: The video thread reads its snapshot
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Link with lld
|
|
run: sh .github/workflows/scripts/use-lld.sh
|
|
|
|
- name: Tree check
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
# The main thread builds a video_frame_info_t and hands it
|
|
# over; a driver that reaches back into config_get_ptr() from
|
|
# frame() or present_last() reads settings the main thread
|
|
# may be writing. TSan found one of those in the null driver
|
|
# rather than anyone noticing it by reading, so this looks
|
|
# for the rest: it walks what those two entry points call
|
|
# and fails on live-state access it can reach. The drivers
|
|
# for platforms with no video thread are listed in the
|
|
# checker with that reason, and the list has to shrink when
|
|
# one of them stops doing it.
|
|
tools/check.sh tools/video_thread_state_check.c
|
|
|
|
drm-vkms-live:
|
|
name: The DRM driver on a real kernel (vkms)
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
env:
|
|
APT_PKGS: libdrm-dev qemu-system-x86 busybox-static zstd cpio
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Apt cache key
|
|
id: apt
|
|
run: sh .github/workflows/scripts/apt-cache-key.sh "$APT_PKGS"
|
|
|
|
- name: Restore apt cache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
restore-keys: ${{ steps.apt.outputs.prefix }}
|
|
|
|
- name: Install dependencies
|
|
timeout-minutes: 10
|
|
run: sh .github/workflows/scripts/apt-install.sh $APT_PKGS
|
|
|
|
- name: Save apt cache
|
|
if: env.APT_CACHE_SAVE == '1'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
|
|
# The kernel the VM boots: Ubuntu's current HWE kernel, fetched
|
|
# once per version rather than every run
|
|
- name: Kernel version
|
|
id: kernel
|
|
run: echo "kver=$(sh samples/gfx/drm_vkms_live/run_vm.sh --kernel-version)" >> "$GITHUB_OUTPUT"
|
|
|
|
- name: Restore kernel
|
|
id: kernel-cache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: samples/gfx/drm_vkms_live/.kernel
|
|
key: vkms-kernel-${{ steps.kernel.outputs.kver }}
|
|
|
|
- name: Build and run drm_vkms_live_test in a VM
|
|
shell: bash
|
|
working-directory: samples/gfx/drm_vkms_live
|
|
run: |
|
|
set -eu
|
|
# gfx/drivers/drm_gfx.c against the kernel itself: Ubuntu's
|
|
# current HWE kernel boots in qemu with vkms, its overlay
|
|
# planes and its writeback connector, and what the CRTC
|
|
# composes is read back and compared with the frame - as it
|
|
# is, turned each way, scanned out of the threaded wrapper's
|
|
# frame memory, flipped without vsync, under the input overlay
|
|
# on a plane of its own, at a CRT modeline, and in RGB565 -
|
|
# and with the screenshot readback. The fake kernel in
|
|
# drm_surface_lifetime cannot say what a kernel refuses: a
|
|
# turn set on its own, outside the commit that places the
|
|
# plane, was refused here.
|
|
make SANITIZER=address,undefined
|
|
make check SANITIZER=address,undefined
|
|
|
|
- name: Save kernel
|
|
if: steps.kernel-cache.outputs.cache-hit != 'true'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: samples/gfx/drm_vkms_live/.kernel
|
|
key: vkms-kernel-${{ steps.kernel.outputs.kver }}
|
|
|
|
surface-requirements:
|
|
name: Producers ask the surface layer
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Link with lld
|
|
run: sh .github/workflows/scripts/use-lld.sh
|
|
|
|
- name: Self-test
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
# A producer outside the allowlist that reads the video flags
|
|
# directly must be caught, and so must an entry whose file has
|
|
# stopped reading them - the second is what makes the list
|
|
# shrink as producers migrate instead of sitting there.
|
|
tools/check.sh tools/surface_requirements_check.c --selftest
|
|
|
|
- name: Tree check
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
# What a producer should emit is the surface layer's answer
|
|
# (gfx_surface_query_requirements), so a decoder has one place
|
|
# to ask rather than three unrelated flags to know about. The
|
|
# files still asking directly are listed in the checker and
|
|
# are migrated one at a time; a new one may not join them.
|
|
tools/check.sh tools/surface_requirements_check.c
|
|
|
|
- name: texture_image request check
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
# image_texture_load reads pix10 as the caller's request for a
|
|
# 10-bit decode, so a stack image filled field by field asks for
|
|
# whatever the stack held and a 16-bit PNG comes back as packed
|
|
# 10-bit words now and then. Every loaded image has its request
|
|
# set first; the self-test proves an unset one is caught.
|
|
tools/check.sh tools/texture_image_init_check.c --selftest
|
|
tools/check.sh tools/texture_image_init_check.c
|
|
|
|
vulkan-swapchain-extent:
|
|
name: Android Vulkan orientation extents
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 10
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
- name: Regression test
|
|
run: make -C samples/gfx/vulkan_swapchain_extent check
|
|
|
|
vulkan-driver-pacing:
|
|
name: Vulkan driver pacing backend (fake VK_NV_low_latency2 and NVAPI)
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
env:
|
|
APT_PKGS: gcc-mingw-w64-x86-64 wine64
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Apt cache key
|
|
id: apt
|
|
run: sh .github/workflows/scripts/apt-cache-key.sh "$APT_PKGS"
|
|
|
|
- name: Restore apt cache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
restore-keys: ${{ steps.apt.outputs.prefix }}
|
|
|
|
- name: Install dependencies
|
|
timeout-minutes: 15
|
|
run: sh .github/workflows/scripts/apt-install.sh $APT_PKGS
|
|
|
|
- name: Save apt cache
|
|
if: env.APT_CACHE_SAVE == '1'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
|
|
- name: Regression test
|
|
run: |
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
# The mode and sleep state machine of the Driver Pacing
|
|
# backend, on fake entry points: one sleep and one bounded
|
|
# wait a content frame, markers on the released frame only,
|
|
# a replaced swapchain starting over, errors falling back.
|
|
make -C samples/gfx/vulkan_driver_pacing check
|
|
make -C samples/gfx/vulkan_driver_pacing clean
|
|
make -C samples/gfx/vulkan_driver_pacing SANITIZER=address,undefined check
|
|
make -C samples/gfx/vulkan_driver_pacing clean
|
|
# Windows: presenting through DXGI, the mode, sleep and
|
|
# markers go through NVAPI (a stand-in nvapi64.dll)
|
|
make -C samples/gfx/vulkan_driver_pacing CC=x86_64-w64-mingw32-gcc RUNNER=wine check
|
|
make -C samples/gfx/vulkan_driver_pacing clean
|
|
|
|
vulkan-upscaler:
|
|
name: Vulkan temporal upscaler glue (lavapipe, stand-in NGX, FSR and XeSS)
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
env:
|
|
APT_PKGS: libvulkan-dev mesa-vulkan-drivers vulkan-validationlayers gcc-mingw-w64-x86-64 wine64
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Apt cache key
|
|
id: apt
|
|
run: sh .github/workflows/scripts/apt-cache-key.sh "$APT_PKGS"
|
|
|
|
- name: Restore apt cache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
restore-keys: ${{ steps.apt.outputs.prefix }}
|
|
|
|
- name: Install dependencies
|
|
timeout-minutes: 15
|
|
run: sh .github/workflows/scripts/apt-install.sh $APT_PKGS
|
|
|
|
- name: Save apt cache
|
|
if: env.APT_CACHE_SAVE == '1'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
|
|
- name: Regression test
|
|
run: |
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
# gfx/common/vulkan_ngx.c, vulkan_ffx.c, vulkan_xess.c, the
|
|
# dispatch of vulkan_upscaler.c and gfx/video_upscaler.c on
|
|
# lavapipe under the validation layer, against stand-in NGX,
|
|
# FidelityFX API and XeSS libraries: the session, the core's
|
|
# request, a frame's feature and dispatch parameters, resizes
|
|
# retiring the old output, refused frames, teardown.
|
|
make -C samples/gfx/vulkan_upscaler check
|
|
make -C samples/gfx/vulkan_upscaler clean
|
|
make -C samples/gfx/vulkan_upscaler SANITIZER=address,undefined check
|
|
make -C samples/gfx/vulkan_upscaler clean
|
|
# Windows: the same glue built with mingw; Wine has no Vulkan
|
|
# device here, so the run only proves the build and the skip
|
|
make -C samples/gfx/vulkan_upscaler CC=x86_64-w64-mingw32-gcc RUNNER=wine check
|
|
make -C samples/gfx/vulkan_upscaler clean
|
|
|
|
d3d12-upscaler:
|
|
name: Direct3D 12 temporal upscaler glue (Wine vkd3d on lavapipe, stand-in NGX, FSR and XeSS)
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 15
|
|
env:
|
|
APT_PKGS: gcc-mingw-w64-x86-64 wine64 xvfb xauth mesa-vulkan-drivers libvulkan1
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Apt cache key
|
|
id: apt
|
|
run: sh .github/workflows/scripts/apt-cache-key.sh "$APT_PKGS"
|
|
|
|
- name: Restore apt cache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
restore-keys: ${{ steps.apt.outputs.prefix }}
|
|
|
|
- name: Install dependencies
|
|
timeout-minutes: 15
|
|
run: sh .github/workflows/scripts/apt-install.sh $APT_PKGS
|
|
|
|
- name: Save apt cache
|
|
if: env.APT_CACHE_SAVE == '1'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
|
|
- name: Regression test
|
|
run: |
|
|
# gfx/common/d3d12_ngx.c, d3d12_ffx.c, d3d12_xess.c, the
|
|
# dispatch of d3d12_upscaler.c and gfx/video_upscaler.c,
|
|
# cross-built with mingw, on Wine's D3D12 (vkd3d over
|
|
# lavapipe, which wants a display) against stand-in NGX,
|
|
# FidelityFX API and XeSS libraries: the session, the core's
|
|
# request, a frame's feature and dispatch on the frame's list
|
|
# with the output read back, resizes retiring the old output,
|
|
# refused frames, teardown.
|
|
export WINEPREFIX="$RUNNER_TEMP/d3d12-upscaler-wine"
|
|
export WINEDEBUG=-all
|
|
make -C samples/gfx/d3d12_upscaler
|
|
timeout 180 xvfb-run -a bash -c '
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
for pass in 1 2; do
|
|
make -C samples/gfx/d3d12_upscaler check
|
|
done
|
|
'
|
|
make -C samples/gfx/d3d12_upscaler clean
|
|
|
|
slang-rect:
|
|
name: Presets read frames where they lie
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
|
|
env:
|
|
APT_PKGS: build-essential libegl-dev libgl-dev libegl-mesa0 libgl1-mesa-dri libspirv-cross-c-shared-dev gcc-mingw-w64-x86-64 wine64
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Apt cache key
|
|
id: apt
|
|
run: sh .github/workflows/scripts/apt-cache-key.sh "$APT_PKGS"
|
|
|
|
- name: Restore apt cache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
restore-keys: ${{ steps.apt.outputs.prefix }}
|
|
|
|
- name: Install dependencies
|
|
timeout-minutes: 15
|
|
run: sh .github/workflows/scripts/apt-install.sh $APT_PKGS
|
|
|
|
- name: Save apt cache
|
|
if: env.APT_CACHE_SAVE == '1'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
|
|
- name: Link with lld
|
|
run: sh .github/workflows/scripts/use-lld.sh
|
|
|
|
- name: Build and run slang_rect_test (ASan + UBSan, llvmpipe)
|
|
shell: bash
|
|
working-directory: samples/gfx/slang_rect
|
|
run: |
|
|
set -eu
|
|
# gfx/drivers_shader/slang_rect.c rewrites a preset's stage
|
|
# to read its frame inside a larger texture. Each test shader,
|
|
# sampled each way, comes back rewritten, left alone or
|
|
# refused as it should and cross-compiles; and drawn by Mesa,
|
|
# every rewritten stage reads the frame bit for bit as the
|
|
# stage it was reads a texture of its own.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1 \
|
|
timeout 600 make check CHECK_ARGS=--require-gl
|
|
make clean
|
|
|
|
- name: Build and run rslc_remap_test (ASan + UBSan, llvmpipe)
|
|
shell: bash
|
|
working-directory: samples/gfx/rslc_remap
|
|
run: |
|
|
set -eu
|
|
# rslc's frame-rectangle remap, which rewrites a stage in the
|
|
# compiler for every destination: each test shader of
|
|
# ../slang_rect, sampled each way, comes back as slang_rect.c
|
|
# says of rslc's own module, prints as GLSL, GLSL ES, HLSL and
|
|
# MSL, places the rectangle past both stages' blocks, fails
|
|
# cleanly out of memory; and drawn by Mesa from rslc's GLSL,
|
|
# every rewritten stage reads the frame bit for bit as the
|
|
# stage it was reads a texture of its own.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1 \
|
|
timeout 600 make check CHECK_ARGS=--require-gl
|
|
make clean
|
|
|
|
- name: Build and run rslc_remap_test (mingw-w64, Wine)
|
|
shell: bash
|
|
working-directory: samples/gfx/rslc_remap
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
make clean CC=x86_64-w64-mingw32-gcc && make -j$(nproc) all CC=x86_64-w64-mingw32-gcc LDFLAGS=-static
|
|
WINEDEBUG=-all timeout 300 wine ./rslc_remap_test.exe
|
|
make clean
|
|
|
|
slang-last-pass-target:
|
|
name: Last shader pass renders to the backbuffer
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
|
|
env:
|
|
APT_PKGS: build-essential libgl-dev gcc-mingw-w64-x86-64 wine64
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Apt cache key
|
|
id: apt
|
|
run: sh .github/workflows/scripts/apt-cache-key.sh "$APT_PKGS"
|
|
|
|
- name: Restore apt cache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
restore-keys: ${{ steps.apt.outputs.prefix }}
|
|
|
|
- name: Install dependencies
|
|
timeout-minutes: 15
|
|
run: sh .github/workflows/scripts/apt-install.sh $APT_PKGS
|
|
|
|
- name: Save apt cache
|
|
if: env.APT_CACHE_SAVE == '1'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
|
|
- name: Build and run slang_hlsl_backend_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/slang_hlsl_backend
|
|
run: |
|
|
set -eu
|
|
# The real slang_process(): rslc hands a pass back as HLSL for
|
|
# the Direct3D 10/11/12 drivers, and a preset's simulated
|
|
# SourceSize reaches the first pass only, from
|
|
# storage that outlives the semantics the D3D drivers copy.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1 \
|
|
timeout 300 make check SANITIZER=address,undefined
|
|
make clean
|
|
|
|
- name: Build and run slang_last_pass_target_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/slang_last_pass_target
|
|
run: |
|
|
set -eu
|
|
# The Vulkan and glcore slang chains give a preset's last pass
|
|
# a render target and a hidden copy to the backbuffer only when
|
|
# scale/format or compiled feedback requires it: viewport at 1.0
|
|
# renders direct; other scales, a preset or shader format, and
|
|
# a read of the last pass's feedback keep the target.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1 \
|
|
timeout 60 make check
|
|
make clean
|
|
|
|
- name: Build and run slang_last_pass_target_test (mingw-w64, Wine)
|
|
shell: bash
|
|
working-directory: samples/gfx/slang_last_pass_target
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
make clean CC=x86_64-w64-mingw32-gcc && make -j$(nproc) all CC=x86_64-w64-mingw32-gcc LDFLAGS="-static -Wl,--gc-sections"
|
|
WINEDEBUG=-all timeout 300 wine ./slang_last_pass_target_test.exe
|
|
make clean
|
|
|
|
- name: Build and run shader_chain_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/shader_chain
|
|
run: |
|
|
set -eu
|
|
# The blob table: a .slang two passes name compiles once, and
|
|
# a chain built while the one it replaces is alive compiles
|
|
# only the passes the live chain does not have.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1 \
|
|
timeout 120 make check
|
|
make clean
|
|
|
|
- name: Build and run shader_chain_test (mingw-w64, Wine)
|
|
shell: bash
|
|
working-directory: samples/gfx/shader_chain
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
make clean CC=x86_64-w64-mingw32-gcc && make -j$(nproc) all CC=x86_64-w64-mingw32-gcc LDFLAGS="-static"
|
|
WINEDEBUG=-all timeout 300 wine ./shader_chain_test.exe
|
|
make clean
|
|
|
|
- name: Build and run gl2/gl3 last_pass_fbo tests (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gl_last_pass_fbo
|
|
run: |
|
|
set -eu
|
|
# The GLSL/Cg render chains of gfx/drivers/gl2.c and gl3.c
|
|
# against a scripted GL and shader backend: the last pass gets
|
|
# an FBO only for a scale other than viewport at 1.0, an FBO
|
|
# format, or the feedback the preset asks of it; a one-pass
|
|
# preset that needs none is drawn with none.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1 \
|
|
timeout 60 make check
|
|
make clean
|
|
|
|
- name: Build and run gl2/gl3 last_pass_fbo tests (mingw-w64, Wine)
|
|
shell: bash
|
|
working-directory: samples/gfx/gl_last_pass_fbo
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
make clean CC=x86_64-w64-mingw32-gcc && make -j$(nproc) all CC=x86_64-w64-mingw32-gcc LDFLAGS=-static
|
|
WINEDEBUG=-all timeout 300 wine ./gl2_last_pass_fbo_test.exe
|
|
WINEDEBUG=-all timeout 300 wine ./gl3_last_pass_fbo_test.exe
|
|
make clean
|
|
|
|
- name: GLES texture loan capability and ownership (mingw-w64, Wine)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
CC=x86_64-w64-mingw32-gcc RUNNER="wine" WINEDEBUG=-all \
|
|
python3 samples/gfx/gles_texture_lend/run.py
|
|
|
|
- name: Build and run gl2_pbo_readback_test (mingw-w64, Wine)
|
|
shell: bash
|
|
working-directory: samples/gfx/gl2_pbo_readback
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
# The Linux runs are in the samples-gfx job; this is the
|
|
# Windows build, the GL 1.1 entry points through import
|
|
# pointers (gl11_fakes.h).
|
|
make clean CC=x86_64-w64-mingw32-gcc && make -j$(nproc) all CC=x86_64-w64-mingw32-gcc LDFLAGS=-static
|
|
WINEDEBUG=-all timeout 300 wine ./gl2_pbo_readback_test.exe
|
|
CC=x86_64-w64-mingw32-gcc RUNNER=wine WINEDEBUG=-all python3 gles3_init_test.py
|
|
make clean
|
|
|
|
- name: Build and run d3d9 last_pass tests (mingw-w64, Wine)
|
|
shell: bash
|
|
working-directory: samples/gfx/d3d9_last_pass
|
|
run: |
|
|
set -eu
|
|
# The preset setup of gfx/drivers/d3d9hlsl.c and d3d9cg.c: a
|
|
# stock pass is appended after the last pass only for a scale
|
|
# other than viewport at 1.0 or a floating-point target.
|
|
# Direct3D 9 is Windows-only, so these run under Wine alone.
|
|
make clean && make -j$(nproc) all
|
|
timeout 300 make check
|
|
make clean
|
|
|
|
- name: Build and run the d3d9 software framebuffer loan test (mingw-w64, Wine)
|
|
shell: bash
|
|
working-directory: samples/gfx/d3d9_sw_fb
|
|
run: |
|
|
set -eu
|
|
# GET_CURRENT_SOFTWARE_FRAMEBUFFER on the D3D9 drivers: the frame
|
|
# texture lent locked, read out of without a copy, a cropped
|
|
# window moved to its corner, and the texture holding the cached
|
|
# frame kept alive until it is retired.
|
|
make clean && make all
|
|
timeout 300 make check
|
|
make clean
|
|
|
|
- name: Build and run d3d9 menu vertex ring tests (mingw-w64, Wine)
|
|
shell: bash
|
|
working-directory: samples/gfx/d3d9_menu_ring
|
|
run: |
|
|
set -eu
|
|
# Each D3D9 driver's display draw against a stand-in device: the
|
|
# menu's vertex ring is dynamic, starts over with DISCARD and
|
|
# never rewrites a vertex a pending draw reads (d3d9_cg's did,
|
|
# and drew whole-screen triangles while a notification was up).
|
|
make clean && make -j$(nproc) all
|
|
timeout 300 make check
|
|
make clean
|
|
|
|
samples-gfx:
|
|
name: Build and run samples/gfx
|
|
runs-on: ubuntu-latest
|
|
# gfx_thumbnail_preview runs as its own job (thumbnail-preview):
|
|
# at 5-9 min it was the longest step here, and with package
|
|
# installation on a slow runner it took the job past 30 min.
|
|
timeout-minutes: 30
|
|
env:
|
|
APT_PKGS: build-essential libx11-dev libxrandr-dev libdrm-dev xserver-xorg-core xserver-xorg-video-dummy x11-xserver-utils libsdl2-dev libxinerama-dev libwayland-dev libegl-dev libgles-dev libgbm-dev gcc-mingw-w64-x86-64 wine64 openbox cmake ninja-build ccache libgl-dev xvfb libdbus-1-dev dbus python3-dbus python3-gi weston xwayland mutter libvkd3d1 mesa-vulkan-drivers fonts-dejavu-core fonts-dejavu-mono
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Apt cache key
|
|
id: apt
|
|
run: sh .github/workflows/scripts/apt-cache-key.sh "$APT_PKGS"
|
|
|
|
- name: Restore apt cache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
restore-keys: ${{ steps.apt.outputs.prefix }}
|
|
|
|
- name: Install dependencies
|
|
timeout-minutes: 20
|
|
run: sh .github/workflows/scripts/apt-install.sh $APT_PKGS
|
|
|
|
- name: Save apt cache
|
|
if: env.APT_CACHE_SAVE == '1'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
|
|
- name: Link with lld
|
|
run: sh .github/workflows/scripts/use-lld.sh
|
|
|
|
- name: Restore ccache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.ccache
|
|
key: ccache-${{ github.workflow }}-${{ github.job }}-${{ github.sha }}
|
|
restore-keys: |
|
|
ccache-${{ github.workflow }}-${{ github.job }}-
|
|
|
|
- name: Enable ccache
|
|
run: |
|
|
set -eu
|
|
export CCACHE_DIR="$HOME/.ccache"
|
|
echo "CCACHE_DIR=$CCACHE_DIR" >> "$GITHUB_ENV"
|
|
echo "/usr/lib/ccache" >> "$GITHUB_PATH"
|
|
ccache -M 250M
|
|
ccache -z
|
|
|
|
- name: Video modeline engine goldens and C89 lane
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
# The engine in gfx/modeline/ replaced the SwitchRes 2.2.1
|
|
# library; tools/modeline_gold.ref is the reference output of
|
|
# the same matrix run through that library. The scoring and
|
|
# timing arithmetic are compared bit-for-bit: any drift in
|
|
# modeline_create/compare/adjust, the presets or the list
|
|
# logic shows up here as a diff rather than as a CRT that
|
|
# loses sync in the field.
|
|
C89="-std=c89 -ansi -pedantic -Werror=pedantic -Wno-long-long"
|
|
C89="$C89 -Werror=declaration-after-statement -Wno-variadic-macros"
|
|
C89="$C89 -D_GNU_SOURCE"
|
|
INC="-Ilibretro-common/include -I. -Ideps"
|
|
# Binaries and outputs go to a scratch dir, never the tree.
|
|
T=$(mktemp -d)
|
|
gcc $C89 $INC tools/modeline_gold.c \
|
|
libretro-common/compat/compat_strl.c -lm -o "$T/modeline_gold"
|
|
"$T/modeline_gold" > "$T/modeline_gold.out"
|
|
diff -u tools/modeline_gold.ref "$T/modeline_gold.out"
|
|
echo "[pass] modeline goldens (C89)"
|
|
gcc -g1 -O1 -fsanitize=address,undefined -fno-sanitize-recover=all \
|
|
-std=gnu89 -D_GNU_SOURCE $INC tools/modeline_gold.c \
|
|
libretro-common/compat/compat_strl.c -lm -o "$T/modeline_gold_san"
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
"$T/modeline_gold_san" > "$T/modeline_gold_san.out"
|
|
diff -u tools/modeline_gold.ref "$T/modeline_gold_san.out"
|
|
echo "[pass] modeline goldens (ASan/UBSan)"
|
|
rm -rf "$T"
|
|
# The consumer and the X11 apply path are C89 too; the
|
|
# full C89_BUILD covers them only after a complete build.
|
|
DEF="-DRARCH_INTERNAL -DHAVE_THREADS -DHAVE_MENU -DHAVE_MODELINE"
|
|
DEF="$DEF -DHAVE_X11 -DHAVE_XRANDR -DHAVE_CONFIGFILE -DHAVE_DYNAMIC"
|
|
for f in gfx/modeline/modeline_core.c \
|
|
gfx/modeline/modeline_monitor.c \
|
|
gfx/modeline/modeline_list.c \
|
|
gfx/modeline/modeline_ini.c \
|
|
gfx/video_crt_switch.c \
|
|
gfx/display_servers/dispserv_x11.c; do
|
|
gcc $C89 $INC $DEF -c "$f" -o /dev/null
|
|
done
|
|
echo "[pass] modeline C89 syntax check"
|
|
|
|
- name: Atomic backend portability of the gfx sources
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
# The gfx sources that use retro_atomic.h have to build on
|
|
# every backend, not just the one this host selects. The
|
|
# extended operations (exchange, CAS, the pointer ops) are
|
|
# offered only where a backend can express them, so a caller
|
|
# that uses one without gating on it builds here and breaks
|
|
# on whichever console lands elsewhere; the counters have to
|
|
# stay warning-clean as statements, which an expression-form
|
|
# fetch_add is not. Both have reached a console lane before
|
|
# this one, hence -Werror.
|
|
INC="-Ilibretro-common/include -I. -Ideps"
|
|
DEF="-DRARCH_INTERNAL -DHAVE_THREADS -DHAVE_MENU -DHAVE_RGUI"
|
|
DEF="$DEF -DHAVE_CONFIGFILE -DHAVE_CHEATS -D_GNU_SOURCE"
|
|
DEF="$DEF -DRETRO_ATOMIC_SUPPRESS_WARNING"
|
|
# The PS2 backend needs ps2sdk's <kernel.h>; the sample under
|
|
# libretro-common/samples/atomic carries a stand-in for it.
|
|
PS2STUB="libretro-common/samples/atomic/retro_atomic_ps2_backend"
|
|
for backend in C11 GCC_NEW SYNC PS2 VOLATILE; do
|
|
EXTRA=""
|
|
if [ "$backend" = "PS2" ]; then EXTRA="-I$PS2STUB/ps2stub"; fi
|
|
for f in gfx/video_thread_wrapper.c \
|
|
gfx/font_driver.c \
|
|
gfx/gfx_thumbnail.c \
|
|
libretro-common/formats/video_player.c \
|
|
libretro-common/queues/task_queue.c; do
|
|
gcc -std=gnu99 -O2 -Wall -Werror $INC $EXTRA $DEF \
|
|
"-DRETRO_ATOMIC_FORCE_$backend" -c "$f" -o /dev/null
|
|
done
|
|
gcc -std=gnu99 -O2 -Wall -Werror $INC $EXTRA $DEF \
|
|
-DHAVE_GFX_WIDGETS "-DRETRO_ATOMIC_FORCE_$backend" \
|
|
-c gfx/gfx_widgets.c -o /dev/null
|
|
# The Vulkan driver, from the Khronos headers the tree carries
|
|
gcc -std=gnu99 -O2 -Wall -Werror $INC -Igfx/include $EXTRA $DEF \
|
|
-DHAVE_VULKAN "-DRETRO_ATOMIC_FORCE_$backend" \
|
|
-c gfx/drivers/vulkan.c -o /dev/null
|
|
echo "[pass] RETRO_ATOMIC_FORCE_$backend"
|
|
done
|
|
echo "[pass] atomic backend portability"
|
|
|
|
- name: Build and run vulkan_extension_count_test (ASan)
|
|
shell: bash
|
|
working-directory: samples/gfx/vulkan_extension_count
|
|
run: |
|
|
set -eu
|
|
# Regression test for the heap-overflow fix in
|
|
# gfx/common/vulkan_common.c::vulkan_find_device_extensions.
|
|
# Pre-fix the function appended the required-extension list
|
|
# twice -- once via memcpy at the top of the body, then
|
|
# again in a per-element loop -- consuming
|
|
# (count_initial + 2*num_required + num_optional) slots in
|
|
# the caller's buffer. vulkan_context_create_device_wrapper
|
|
# sized its malloc for (count_initial + num_required +
|
|
# num_optional) entries, so a libretro core using the Vulkan
|
|
# HW context-negotiation interface against a GPU exposing at
|
|
# least one optional extension hit a one-element heap-buffer-
|
|
# overflow (8 bytes on 64-bit) at the end of the malloc'd
|
|
# block. Build under AddressSanitizer so any reintroduction
|
|
# of the duplicate write is caught at the bounds level. If
|
|
# vulkan_common.c amends the append-to-enabled[] block, the
|
|
# verbatim copy in vulkan_extension_count_test.c must follow.
|
|
make clean SANITIZER=address && make -j$(nproc) all SANITIZER=address
|
|
test -x vulkan_extension_count_test
|
|
timeout 60 ./vulkan_extension_count_test
|
|
echo "[pass] vulkan_extension_count_test"
|
|
|
|
- name: Build and run video_views_test (ASan+UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/video_views
|
|
run: |
|
|
set -eu
|
|
# gfx/video_views.c decides which rectangle of the core's
|
|
# frame lands where, and maps touch back through that layout.
|
|
# Pure logic, so every mode and edge case runs here.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
test -x video_views_test
|
|
timeout 60 ./video_views_test
|
|
echo "[pass] video_views_test"
|
|
|
|
- name: Build and run video_windows_test (ASan+UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/video_windows
|
|
run: |
|
|
set -eu
|
|
make clean all SANITIZER=address,undefined
|
|
timeout 60 ./video_windows_test
|
|
echo "[pass] video_windows_test"
|
|
|
|
- name: Build and run video_xr_test (ASan+UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/video_xr
|
|
run: |
|
|
set -eu
|
|
# gfx/video_xr.c places a core's screens in a headset, sizes
|
|
# their images and maps a pointer on them back to the frame.
|
|
# Pure maths, so every layout and edge runs here.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
test -x video_xr_test
|
|
timeout 60 ./video_xr_test
|
|
echo "[pass] video_xr_test"
|
|
|
|
- name: Build and run vulkan_swapchain_clamp_test (ASan)
|
|
shell: bash
|
|
working-directory: samples/gfx/vulkan_swapchain_clamp
|
|
run: |
|
|
set -eu
|
|
# Regression test for the unclamped-swapchain-image-count
|
|
# fix in gfx/common/vulkan_common.c::vulkan_create_swapchain.
|
|
# Pre-fix the two vkGetSwapchainImagesKHR calls (count
|
|
# query + image fill) had no clamp between them, so a driver
|
|
# returning more than VULKAN_MAX_SWAPCHAIN_IMAGES (16) images
|
|
# on the second call wrote past context.swapchain_images[16]
|
|
# and every loop bounded by num_swapchain_images walked past
|
|
# its compile-time-sized companion array (~12 such loops
|
|
# across init/deinit/textures/buffers/descriptor pools/
|
|
# command buffers/readback and direct vk->swapchain[i]
|
|
# uses). Build under AddressSanitizer so any reintroduction
|
|
# of either the request-side or the post-create clamp is
|
|
# caught at the bounds level. If vulkan_common.c amends
|
|
# the cap or the post-create clamp, the verbatim copies in
|
|
# vulkan_swapchain_clamp_test.c must follow.
|
|
make clean SANITIZER=address && make -j$(nproc) all SANITIZER=address
|
|
test -x vulkan_swapchain_clamp_test
|
|
timeout 60 ./vulkan_swapchain_clamp_test
|
|
echo "[pass] vulkan_swapchain_clamp_test"
|
|
|
|
- name: Build and run vulkan_output_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/vulkan_output
|
|
run: |
|
|
set -eu
|
|
# The second output's swapchain (vulkan_output_* in
|
|
# gfx/common/vulkan_common.c), the real code against a fake
|
|
# device: nothing it does in a frame waits, and nothing is
|
|
# destroyed before the fence that covers it.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
test -x vulkan_output_test
|
|
timeout 60 ./vulkan_output_test
|
|
echo "[pass] vulkan_output_test"
|
|
|
|
- name: Build and run vulkan_chain_alloc_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/vulkan_chain_alloc
|
|
run: |
|
|
set -eu
|
|
# gfx/drivers_shader/shader_vulkan.c's filter chain creation,
|
|
# Vulkan through the symbol wrapper pointed at mocks, with
|
|
# each allocation it makes failed in turn: a chain comes back
|
|
# with every pass and every deferred-disposal queue, or not
|
|
# at all, and nothing is left behind. A replay of the last
|
|
# frame's final pass reads what that pass read, and a chain
|
|
# made to replay keeps one history image more.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./vulkan_chain_alloc_test
|
|
make clean
|
|
echo "[pass] vulkan_chain_alloc_test"
|
|
|
|
- name: Build and run gl3_shader_status_test and gl3_chain_alloc_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gl3_shader_status
|
|
run: |
|
|
set -eu
|
|
# gfx/drivers_shader/shader_gl3.c with GL through the glsym
|
|
# table pointed at mocks: shader status checks, the chain's
|
|
# allocations failed in turn, and a replay of the last frame's
|
|
# final pass reading the textures, at the generations, that
|
|
# pass read, the history keeping a spare.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 make check SANITIZER=address,undefined
|
|
make clean
|
|
echo "[pass] gl3_shader_status_test, gl3_chain_alloc_test"
|
|
|
|
- name: Build and run vulkan_texture_size_test (ASan)
|
|
shell: bash
|
|
working-directory: samples/gfx/vulkan_texture_size
|
|
run: |
|
|
set -eu
|
|
# Regression test for the 32-bit-overflow fix in
|
|
# gfx/drivers/vulkan.c::vulkan_create_texture's staging-
|
|
# buffer sizing. Pre-fix the buffer_info.size calculation
|
|
# (buffer_width * height) was unsigned*unsigned in 32-bit
|
|
# before being widened to VkDeviceSize on assignment. With
|
|
# dimensions large enough to wrap (e.g. 65536x16385x4 ->
|
|
# 0x1_0004_0000 truncates to 0x40000), the staging buffer
|
|
# was malloc'd at the wrapped (small) size while the per-row
|
|
# upload memcpy loop walked the full width x height,
|
|
# writing past the mapped region into adjacent heap memory.
|
|
# Reachable from libretro cores supplying oversized
|
|
# retro_framebuffer dimensions and from vulkan_load_texture
|
|
# / vulkan_set_texture_frame. Build under AddressSanitizer
|
|
# so any reintroduction of the 32-bit arithmetic is caught
|
|
# at the bounds level. If vulkan.c amends the size
|
|
# calculation or upload-loop strides, the verbatim copies
|
|
# in vulkan_texture_size_test.c must follow.
|
|
make clean SANITIZER=address && make -j$(nproc) all SANITIZER=address
|
|
test -x vulkan_texture_size_test
|
|
timeout 60 ./vulkan_texture_size_test
|
|
echo "[pass] vulkan_texture_size_test"
|
|
|
|
- name: Build and run vulkan_views_target_test
|
|
shell: bash
|
|
working-directory: samples/gfx/vulkan_views_target
|
|
run: |
|
|
set -eu
|
|
# gfx/drivers/vulkan.c's retained view targets and headset
|
|
# draw, copied out of the driver, against a mocked device: a
|
|
# failed allocation is not retried at the same size, a resize
|
|
# waits while both retired slots are full, and each headset
|
|
# screen is drawn straight into the runtime's image, a repeat
|
|
# ending no chain frame.
|
|
make clean && timeout 60 make check
|
|
echo "[pass] vulkan_views_target_test"
|
|
|
|
- name: Build and run gl3_xr_draw_test
|
|
shell: bash
|
|
working-directory: samples/gfx/gl3_xr_draw
|
|
run: |
|
|
set -eu
|
|
# gfx/drivers/gl3.c's headset draw, copied out of the driver,
|
|
# with GL stubbed: each screen straight into the runtime's
|
|
# texture, and a screen moved or resized while paused drawn
|
|
# again without ending a chain frame.
|
|
make clean && timeout 60 make check
|
|
echo "[pass] gl3_xr_draw_test"
|
|
|
|
- name: GLES persistent loan pixel oracle (Mesa EGL)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
T=$(mktemp -d)
|
|
cc -std=gnu89 -O1 -g1 -Wall -Werror=implicit-function-declaration \
|
|
-DHAVE_OPENGLES -DHAVE_OPENGLES3 -DHAVE_EGL \
|
|
-Ilibretro-common/include -Igfx/include \
|
|
samples/gfx/gles_texture_lend/gpu_test.c gfx/common/gl_common.c \
|
|
libretro-common/glsym/glsym_es3.c libretro-common/glsym/rglgen.c \
|
|
-lEGL -lGLESv2 -o "$T/gles_lend"
|
|
EGL_PLATFORM=surfaceless LIBGL_ALWAYS_SOFTWARE=1 "$T/gles_lend"
|
|
rm -rf "$T"
|
|
|
|
- name: GLES texture loan capability and ownership (ASan + UBSan)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" \
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=halt_on_error=1 \
|
|
python3 samples/gfx/gles_texture_lend/run.py
|
|
|
|
- name: Build and run gl2_pbo_readback_test (plain, ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gl2_pbo_readback
|
|
run: |
|
|
set -eu
|
|
# gfx/drivers/gl2.c's PBO readback ring against a scripted
|
|
# GL: a slot is read only once the fence placed after its
|
|
# readback has signalled, and never waited on in the map. A
|
|
# read that maps a pending slot, waits in glClientWaitSync
|
|
# or calls glFinish fails the run; teardown must delete
|
|
# every fence it issued.
|
|
make clean check
|
|
echo "[pass] gl2_pbo_readback_test"
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./gl2_pbo_readback_test
|
|
echo "[pass] gl2_pbo_readback_test (ASan)"
|
|
|
|
- name: HDR display metadata contract (ASan/UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/hdr_metadata
|
|
run: |
|
|
set -eu
|
|
make clean
|
|
make check SANITIZER=address,undefined
|
|
CFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer' python3 run.py
|
|
CFLAGS='-fsanitize=thread -fno-omit-frame-pointer' python3 run.py
|
|
CFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer' python3 ownership.py
|
|
|
|
- name: Menu framebuffer loans (ASan/UBSan, TSan)
|
|
shell: bash
|
|
run: |
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
CFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer' python3 samples/gfx/threaded_video/menu_framebuffer.py
|
|
CFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer' python3 samples/gfx/threaded_video/vulkan_menu.py
|
|
CFLAGS='-fsanitize=thread -fno-omit-frame-pointer' python3 samples/gfx/threaded_video/menu_framebuffer.py
|
|
CFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer' python3 samples/gfx/threaded_video/lend_refresh.py
|
|
CFLAGS='-fsanitize=thread -fno-omit-frame-pointer' python3 samples/gfx/threaded_video/lend_refresh.py
|
|
CC=x86_64-w64-mingw32-gcc RUNNER=wine python3 samples/gfx/threaded_video/lend_refresh.py
|
|
|
|
- name: D3D Slang HDR and SwapCount bindings (ASan/UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/slang_hdr_bindings
|
|
run: |
|
|
set -eu
|
|
# Compile both loaders' actual uniform maps from
|
|
# gfx/drivers/d3d10.c, gfx/drivers/d3d11.c and gfx/drivers/d3d12.c.
|
|
CFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer' python3 run.py
|
|
CFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer' python3 output_state.py
|
|
CFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer' python3 peak.py
|
|
CFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer' python3 min_nits.py
|
|
CFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer' python3 tonemap_operator.py
|
|
CFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer' python3 source_hdr.py
|
|
CFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer' python3 passthrough.py
|
|
CFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer' python3 ../hdr_metadata/framebuffer_format.py
|
|
CFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer' python3 metal.py
|
|
CFLAGS='-fsanitize=address,undefined -fno-omit-frame-pointer' python3 metal_constants.py
|
|
|
|
- name: Build and run slang_texture_index_bounds_test (ASan)
|
|
shell: bash
|
|
working-directory: samples/gfx/slang_texture_index_bounds
|
|
run: |
|
|
set -eu
|
|
# Regression test for the texture-semantic index-bounds
|
|
# fix in gfx/drivers_shader/slang_process.cpp::
|
|
# validate_texture_semantic_index(). Pre-fix only
|
|
# SLANG_TEXTURE_SEMANTIC_PASS_OUTPUT was bounded against
|
|
# reflection->pass_number; ORIGINAL_HISTORY, PASS_FEEDBACK
|
|
# and USER had no upper bound on their array index. The
|
|
# index suffix in arrayed semantic names like
|
|
# `OriginalHistory42` is parsed via strtoul in
|
|
# slang_name_to_texture_semantic_array() and propagates
|
|
# into the downstream resize_minimum() calls in
|
|
# set_ubo_texture_offset() and the direct sampler-binding
|
|
# loop. A malicious slang shader declaring
|
|
# `OriginalHistory4294967294` makes std::vector::resize
|
|
# request ~128 GiB, throwing std::bad_alloc which
|
|
# propagates unhandled out of the filter-chain create
|
|
# path, terminating the process. Reachable from any
|
|
# malicious slang preset (downloaded via Online Updater
|
|
# or shipped third-party). Build under AddressSanitizer
|
|
# so any reintroduction is caught at the bounds level.
|
|
# If slang_process.cpp amends the cap table or the
|
|
# dispatch structure, the verbatim copy in
|
|
# slang_texture_index_bounds_test.c must follow.
|
|
make clean SANITIZER=address && make -j$(nproc) all SANITIZER=address
|
|
test -x slang_texture_index_bounds_test
|
|
timeout 60 ./slang_texture_index_bounds_test
|
|
echo "[pass] slang_texture_index_bounds_test"
|
|
|
|
- name: Build and run shader_dir_cycle_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/shader_dir_cycle
|
|
run: |
|
|
set -eu
|
|
# Which folder the Next/Previous Shader hotkeys cycle, over a
|
|
# shader tree built on disk, with gfx/video_shader_parse.c
|
|
# compiled for real and only the frontend stubbed.
|
|
#
|
|
# The folder is chosen from the loaded preset, and the cases
|
|
# that decide it are all shapes the menu itself writes:
|
|
# retroarch.slangp and the core auto preset are simple
|
|
# presets (#reference), so they stand in for what they point
|
|
# at, while a preset the user loaded as itself anchors where
|
|
# it sits however it is written - "Save Preset As" emits a
|
|
# #reference too, and following those would cycle a preset's
|
|
# source folder instead of the folder it was saved into.
|
|
#
|
|
# Two orderings are what this pins. Following a reference
|
|
# one hop further than the wrapper puts the cycling in a
|
|
# folder the user never loaded from; and re-deriving the
|
|
# anchor from a preset the hotkey itself just applied walks
|
|
# back to whatever that preset references, so an entry that
|
|
# is a simple preset traps the cycling on the pair forever.
|
|
# The setting is also toggled mid-run: the list is built
|
|
# under one value and must be dropped when it changes, or
|
|
# turning it on never leaves the root it was cycling.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
test -x shader_dir_cycle_test
|
|
# The tree is built under a scratch dir, never in the repo.
|
|
T=$(mktemp -d)
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
timeout 60 ./shader_dir_cycle_test "$T"
|
|
rm -rf "${T:?}"
|
|
echo "[pass] shader_dir_cycle_test"
|
|
|
|
- name: Build and run vulkan_mailbox_init_leak_test (ASan + LSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/vulkan_mailbox_init_leak
|
|
run: |
|
|
set -eu
|
|
# Regression test for the partial-init leak fix in
|
|
# gfx/common/vulkan_common.c::vulkan_emulated_mailbox_init.
|
|
# The function brings up three things in sequence (two
|
|
# eventcounts and the acquire thread; a condition variable,
|
|
# a mutex and the thread when this was written) and used to
|
|
# return `false` directly when the second or third failed,
|
|
# leaking what the earlier ones had allocated. Both
|
|
# production call sites in vulkan_create_swapchain ignore
|
|
# the return value, so an init failure also left the
|
|
# mailbox half built while VK_DATA_FLAG_EMULATING_MAILBOX
|
|
# was still set, setting up a crash the next time
|
|
# vulkan_acquire_next_image routed into the emulated path.
|
|
# Fix routes every early failure through `goto error` to a
|
|
# single deinit call, which is safe on a partly built
|
|
# mailbox and ends with a memset, leaving the struct in the
|
|
# same shape the deinit-on-shutdown path produces and
|
|
# tripping the existing `mailbox.swapchain == VK_NULL_
|
|
# HANDLE` guard at vulkan_acquire_next_image. Build under
|
|
# AddressSanitizer with leak detection so any
|
|
# reintroduction is caught at the leak level. If
|
|
# vulkan_common.c amends the init or deinit, the verbatim
|
|
# copies in vulkan_mailbox_init_leak_test.c must follow.
|
|
make clean SANITIZER=address && make -j$(nproc) all SANITIZER=address
|
|
test -x vulkan_mailbox_init_leak_test
|
|
ASAN_OPTIONS=detect_leaks=1 timeout 60 \
|
|
./vulkan_mailbox_init_leak_test
|
|
echo "[pass] vulkan_mailbox_init_leak_test"
|
|
|
|
- name: Build and run vulkan_ctx_double_free_test (ASan)
|
|
shell: bash
|
|
working-directory: samples/gfx/vulkan_ctx_double_free
|
|
run: |
|
|
set -eu
|
|
# Regression test for the double-free / use-after-free
|
|
# fix in the Vulkan context drivers' set_video_mode error
|
|
# paths: gfx/drivers_context/wayland_vk_ctx.c,
|
|
# w_vk_ctx.c, x_vk_ctx.c. Pre-fix each set_video_mode
|
|
# called its own destroy()/destroy_resources()+free() on
|
|
# ctx_data before returning false; the caller in
|
|
# gfx/drivers/vulkan.c::vulkan_init then ran
|
|
# vulkan_free()->ctx_driver->destroy(ctx_data) on the
|
|
# already-freed pointer (UAF read of struct fields, then
|
|
# a second free of the same allocation). Reachable from
|
|
# vulkan_surface_create() failure (missing extension /
|
|
# driver issue), Wayland's set_video_mode_common_*
|
|
# helpers failing, X11's XGetVisualInfo returning NULL,
|
|
# or win32_set_video_mode failing. Cocoa (cocoa_vk_ctx)
|
|
# and Android (android_vk_ctx) already handle this
|
|
# correctly by returning false without freeing -- the
|
|
# fix makes Wayland/Win32/X11 match. Build under
|
|
# AddressSanitizer so any reintroduction is caught at
|
|
# the bounds level (UAF + double-free both fire). If
|
|
# any of the three context drivers amends the
|
|
# set_video_mode error path to once again destroy
|
|
# ctx_data, the verbatim copy in
|
|
# vulkan_ctx_double_free_test.c must follow.
|
|
make clean SANITIZER=address && make -j$(nproc) all SANITIZER=address
|
|
test -x vulkan_ctx_double_free_test
|
|
timeout 60 ./vulkan_ctx_double_free_test
|
|
echo "[pass] vulkan_ctx_double_free_test"
|
|
|
|
- name: Build and run gfx_thumbnail_status_atomic_test (TSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gfx_thumbnail_status_atomic
|
|
run: |
|
|
set -eu
|
|
# Regression test for the cross-thread thumbnail status
|
|
# synchronisation in gfx/gfx_thumbnail.c. The .status
|
|
# field is read by the video thread (gfx_thumbnail_draw)
|
|
# and written by the upload-callback thread
|
|
# (gfx_thumbnail_handle_upload), with a release-store /
|
|
# acquire-load pairing that guards the texture / width /
|
|
# height fields published alongside the AVAILABLE
|
|
# status. Pre-port the file used a hand-rolled
|
|
# __atomic_* / _Interlocked / volatile shim. Post-port
|
|
# the .status field is retro_atomic_int_t and the
|
|
# accesses route through retro_atomic_*_int macros.
|
|
#
|
|
# ThreadSanitizer is the right validator for this test:
|
|
# it instruments every atomic load and store and would
|
|
# flag a missing-barrier regression as a data race, even
|
|
# on x86 TSO where the hardware otherwise hides the bug.
|
|
# ASan/UBSan would not catch barrier removal at all
|
|
# (no out-of-bounds access, no UB). The test also
|
|
# carries static-assertion checks that validate the
|
|
# struct layout invariant (.status size == sizeof(int))
|
|
# so the field's atomic type does not change the
|
|
# gfx_thumbnail_t ABI. If gfx_thumbnail.c amends
|
|
# GFX_THUMB_STATUS_STORE / GFX_THUMB_STATUS_LOAD or the
|
|
# .status field type, the verbatim copies in
|
|
# gfx_thumbnail_status_atomic_test.c must follow.
|
|
#
|
|
# STRESS_ITERS is bounded here because the default of
|
|
# 1000000 measures ~145 s on a single core before TSan
|
|
# multiplies it, and this step allows 60 s. 20000 runs
|
|
# in ~2.4 s under TSan on one core and still routes every
|
|
# round trip through the instrumented release/acquire
|
|
# pair; the default stays at 1000000 for local soak runs.
|
|
make clean SANITIZER=thread EXTRA_CFLAGS="-DSTRESS_ITERS=20000" && make -j$(nproc) all SANITIZER=thread EXTRA_CFLAGS="-DSTRESS_ITERS=20000"
|
|
test -x gfx_thumbnail_status_atomic_test
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 60 \
|
|
./gfx_thumbnail_status_atomic_test
|
|
echo "[pass] gfx_thumbnail_status_atomic_test"
|
|
|
|
- name: Build and run gfx_widgets_msg_queue_race_test (TSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gfx_widgets_msg_queue_race
|
|
run: |
|
|
set -eu
|
|
# The widget message ring in gfx/gfx_widgets.c is a lock-free
|
|
# single-producer / single-consumer queue: the main thread
|
|
# publishes through msg_queue_tail, the widgets' owner pops
|
|
# through msg_queue_head, each cursor released by its writer
|
|
# and acquired by the other side, and the slot pointer rides
|
|
# that pairing. The test builds the production push and pop
|
|
# out of gfx_widgets.c itself and drives them from one producer
|
|
# thread and one consumer thread, checking order, loss, and the
|
|
# full-ring refusal. ThreadSanitizer is the validator that
|
|
# matters: a store to the slot array outside the release /
|
|
# acquire pairing, or a cursor published before its slot is
|
|
# written, is a race it reports and the value checks may not.
|
|
# halt_on_error=1 fails the run on the first race.
|
|
make clean SANITIZER=thread && make -j$(nproc) all SANITIZER=thread
|
|
test -x gfx_widgets_msg_queue_race_test
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 60 \
|
|
./gfx_widgets_msg_queue_race_test
|
|
echo "[pass] gfx_widgets_msg_queue_race_test"
|
|
|
|
- name: Build and run gfx_animation_sanitize_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gfx_animation_sanitize
|
|
run: |
|
|
set -eu
|
|
# Regression test for the stack overflow in
|
|
# gfx/gfx_animation.c::gfx_animation_ticker(). Pre-fix it
|
|
# passed PATH_MAX_LENGTH to utf8cpy() as the destination
|
|
# size, in all five places it copies, regardless of what the
|
|
# caller had provided. utf8cpy()'s second argument is a
|
|
# size in BYTES; ticker->len, which the surrounding logic
|
|
# uses, is a width in GLYPHS -- so the guard
|
|
# `str_len <= ticker->len` compared glyph counts and then
|
|
# copied bytes, and one glyph of CJK or emoji is three or
|
|
# four of them. PATH_MAX_LENGTH is 512 and not one of the
|
|
# 27 call sites in tree provides that much (mostly
|
|
# NAME_MAX_LENGTH, 64 bytes in one RGUI case), so the
|
|
# function was licensed to write 511 bytes plus a
|
|
# terminator into any of them. Reachable from content: RGUI
|
|
# sets ticker.len from term_layout.width and ticker.s to a
|
|
# NAME_MAX_LENGTH stack buffer, so a playlist entry with a
|
|
# long enough Japanese, Cyrillic or emoji title overran it.
|
|
# Fixed by giving the struct an s_len field carrying the
|
|
# destination size in bytes, mirroring the dst_str_len that
|
|
# gfx_animation_ticker_smooth() had all along.
|
|
#
|
|
# AddressSanitizer is the validator: the test allocates
|
|
# every ticker destination at exactly the length it hands
|
|
# over, so a single byte past the end is a report rather
|
|
# than a stomp on whatever followed. It also sweeps every
|
|
# easing type through push/update/expire, frees animation
|
|
# subjects while their animation is in flight, and deinits
|
|
# with animations still running. If gfx_animation.c amends
|
|
# the destination sizing, this goes red.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
test -x gfx_animation_sanitize_test
|
|
ASAN_OPTIONS=detect_leaks=1 timeout 120 \
|
|
./gfx_animation_sanitize_test
|
|
echo "[pass] gfx_animation_sanitize_test"
|
|
|
|
|
|
- name: Build and run gfx_widgets_sanitize_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gfx_widgets_sanitize
|
|
run: |
|
|
set -eu
|
|
# Sanitizer sweep of gfx/gfx_widgets.c, focused on the
|
|
# message queue: it has the same shape as the
|
|
# gfx_animation ticker overflow -- a caller-supplied
|
|
# string, internal fixed buffers, and arithmetic that has
|
|
# to keep glyphs and bytes apart. Multi-byte text,
|
|
# over-long text, text with no break opportunity, embedded
|
|
# newlines and empty strings across every message/title
|
|
# pairing, a 256-message overflow to drive eviction and
|
|
# free, and eight init/deinit cycles with messages still
|
|
# outstanding at teardown.
|
|
#
|
|
# Like the thumbnail sweep this pins no historical defect
|
|
# and is a tripwire. Two things decide whether it sweeps
|
|
# anything: the font metric stubs return a non-zero
|
|
# proportional width (a zero-width font makes every "does
|
|
# this fit" test succeed and the layout paths are never
|
|
# entered), and gfx_widgets_init() returning false makes
|
|
# the whole run vacuous -- hence the asserted push and
|
|
# frame counters.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
test -x gfx_widgets_sanitize_test
|
|
ASAN_OPTIONS=detect_leaks=1 timeout 120 \
|
|
./gfx_widgets_sanitize_test
|
|
echo "[pass] gfx_widgets_sanitize_test (ASan)"
|
|
make clean SANITIZER=thread && make -j$(nproc) all SANITIZER=thread
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 120 \
|
|
./gfx_widgets_sanitize_test
|
|
echo "[pass] gfx_widgets_sanitize_test (TSan)"
|
|
- name: Build and run widget snapshot ownership test
|
|
shell: bash
|
|
working-directory: samples/gfx/widget_snapshot_ownership
|
|
run: |
|
|
set -eu
|
|
make clean
|
|
make
|
|
timeout 60 ./widget_snapshot_ownership_test
|
|
make clean
|
|
|
|
- name: Build and run gfx_widgets_task_bar_stack_test (ASan + UBSan, TSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gfx_widgets_task_bar_stack
|
|
run: |
|
|
set -eu
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
test -x gfx_widgets_task_bar_stack_test
|
|
ASAN_OPTIONS=detect_leaks=1 timeout 120 \
|
|
./gfx_widgets_task_bar_stack_test
|
|
echo "[pass] gfx_widgets_task_bar_stack_test (ASan)"
|
|
make clean SANITIZER=thread && make -j$(nproc) all SANITIZER=thread
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 120 \
|
|
./gfx_widgets_task_bar_stack_test
|
|
echo "[pass] gfx_widgets_task_bar_stack_test (TSan)"
|
|
- name: Build and run gfx_widgets_task_gate_test (ASan + UBSan, TSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gfx_widgets_task_gate
|
|
run: |
|
|
set -eu
|
|
# A finished task retires through the widgets' push while
|
|
# the video thread frees the widget bound to it: the task
|
|
# queue must not read the link the widgets clear, and a
|
|
# muted task's empty retirement puts nothing up.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 timeout 120 ./gfx_widgets_task_gate_test
|
|
echo "[pass] gfx_widgets_task_gate_test (ASan)"
|
|
make clean SANITIZER=thread && make -j$(nproc) all SANITIZER=thread
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 120 ./gfx_widgets_task_gate_test
|
|
echo "[pass] gfx_widgets_task_gate_test (TSan)"
|
|
- name: Build and run load_content_icon_test (ASan + UBSan, TSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gfx_widget_load_content_animation
|
|
run: |
|
|
set -eu
|
|
# The load content animation started off the widgets' thread
|
|
# still finds and loads its icon.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=halt_on_error=1 timeout 120 ./load_content_icon_test
|
|
echo "[pass] load_content_icon_test (ASan)"
|
|
make clean SANITIZER=thread && make -j$(nproc) all SANITIZER=thread
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 120 ./load_content_icon_test
|
|
echo "[pass] load_content_icon_test (TSan)"
|
|
- name: Build and run win32_msg_route_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/win32_msg_route
|
|
run: |
|
|
set -eu
|
|
# What the Win32 window procedure does with a message.
|
|
# gfx/common/win32_common.c had twelve window procedures, one
|
|
# for each video family times each kind of input driver,
|
|
# built on three copies of one long switch. They are one
|
|
# procedure now, which asks win32_msg_route() what to do with
|
|
# a message. The three switches are written out in the test
|
|
# as they stood, and for every message - with and without the
|
|
# hotplug timer's id, on the desktop and on the Xbox, with
|
|
# and without threads - the route must be the steps the
|
|
# switch for that kind of input driver took. Raw input's
|
|
# keys made into keyboard events a second time, which is
|
|
# what routing them like another driver's would do, is put
|
|
# back and asserted to be caught.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 120 ./win32_msg_route_test
|
|
make clean
|
|
echo "[pass] win32_msg_route_test (ASan)"
|
|
- name: Build and run win32_screensaver_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/win32_screensaver
|
|
run: |
|
|
set -eu
|
|
# win32_suspend_screensaver() from gfx/common/win32_common.c,
|
|
# compiled from the shipping text against a fake kernel32
|
|
# that counts power requests the way Windows does. Every
|
|
# video driver init asks to hold the screensaver off, so the
|
|
# one request must be created once and set once however
|
|
# often it is asked, and turning the setting off must free
|
|
# the display. Before Windows 7 the thread execution state
|
|
# must be continuous to hold anything.
|
|
make clean
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
make SANITIZER=address,undefined check
|
|
make clean
|
|
echo "[pass] win32_screensaver_test (ASan)"
|
|
- name: Build and run win32_drag_drop_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/win32_drag_drop
|
|
run: |
|
|
set -eu
|
|
# Drag and drop from Windows 95 to current releases, through
|
|
# the setup in frontend/drivers/platform_win32.c and the drop
|
|
# handler in ui/drivers/ui_win32.c, compiled from the
|
|
# shipping text against a fake shell32, user32 and dwmapi.
|
|
# Drops must be switched on with or without dwmapi, DWM setup
|
|
# must run once, an elevated window must let the drop
|
|
# messages through where Windows filters them, and the files
|
|
# must arrive through DragQueryFileW or, where that is
|
|
# missing or reads nothing, DragQueryFileA.
|
|
make clean
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
make SANITIZER=address,undefined check
|
|
make clean
|
|
echo "[pass] win32_drag_drop_test (ASan)"
|
|
- name: Build and run display_servers_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/display_servers
|
|
run: |
|
|
set -eu
|
|
# Regression test for the XRandR query handling in
|
|
# gfx/display_servers/dispserv_x11.c. Pre-fix every query
|
|
# was treated as infallible: XRRGetOutputInfo() returns NULL
|
|
# for an output the server cannot describe and
|
|
# info->connection was read straight off it; likewise
|
|
# XRRGetCrtcInfo() and crtc->width, XRRGetScreenResources(),
|
|
# and XRRGetScreenInfo() feeding a XRRFreeScreenConfigInfo()
|
|
# that is not NULL-tolerant. On any X server advertising
|
|
# RandR without a usable output -- Xvfb, a headless box, a
|
|
# display with everything disconnected -- RetroArch died
|
|
# inside video_display_server_init() before drawing
|
|
# anything. Both orientation walks also indexed
|
|
# screen->crtcs with the output's ncrtc, reading past the
|
|
# end and then configuring a crtc other than the one whose
|
|
# geometry had just been read.
|
|
#
|
|
# The test stubs libXrandr and the handful of Xlib entry
|
|
# points the driver reaches, so it needs the headers but no
|
|
# X server, no display and no RandR extension: what is under
|
|
# test is what happens when the queries FAIL, and a working
|
|
# display is exactly the environment that cannot produce
|
|
# that. The stub numbers screen crtcs from 100 and output
|
|
# crtcs from 200 so which array was walked is read off the
|
|
# call log, and tracks every pointer it hands out so a
|
|
# double free or leak is attributed to the case that caused
|
|
# it. If dispserv_x11.c amends the query handling, this
|
|
# goes red.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
test -x display_servers_test
|
|
timeout 60 ./display_servers_test
|
|
echo "[pass] display_servers_test"
|
|
|
|
- name: Build and run dispserv_gx_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/dispserv_gx
|
|
run: |
|
|
set -eu
|
|
# The GameCube/Wii display server (dispserv_gx.c and the mode
|
|
# rules in dispserv_gx_modes.c), linked as they ship against
|
|
# stubbed libogc queries, on every TV standard: the list holds
|
|
# only modes the standard shows, one of them current, and each
|
|
# entry - through the label the menu prints and the parse the
|
|
# menu does - selects exactly that mode, stores its config id
|
|
# and reports the refresh rate it listed.
|
|
#
|
|
# The menu's parse used to run strtoul() inside
|
|
# VIDEO_SCALE_PUT_H, whose PACK evaluates its argument twice;
|
|
# the second call returned 0 and every pick from the list
|
|
# went out as Wx0. Guard the shape as well as the behaviour.
|
|
if grep -nE 'VIDEO_SCALE_(PUT_[WH]|PACK)\([^;]*strto' \
|
|
../../../menu/cbs/menu_cbs_ok.c; then
|
|
echo "::error::strtoul inside a VIDEO_SCALE macro runs twice"
|
|
exit 1
|
|
fi
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./dispserv_gx_test
|
|
make clean
|
|
|
|
- name: Build and run dispserv_ps3_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/dispserv_ps3
|
|
run: |
|
|
set -eu
|
|
# The PS3 display server (dispserv_ps3.c and the mode rules in
|
|
# dispserv_ps3_modes.c), linked as they ship against stubbed
|
|
# PSL1GHT video output queries, for displays taking different
|
|
# sets of modes with the system menu in each: the list holds
|
|
# exactly what the display takes with the mode in use marked,
|
|
# each entry - through the menu's label and parse - selects
|
|
# that mode and reinitialises the driver once, and
|
|
# ps3_display_server_resolution(), which the RSX driver
|
|
# configures the output from, follows the choice (before, the
|
|
# RSX driver ignored the setting and kept the system menu's
|
|
# mode) and falls back to the system mode when the display
|
|
# does not take it.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./dispserv_ps3_test
|
|
make clean
|
|
|
|
- name: Build and run rsx_alloc_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/rsx_alloc
|
|
run: |
|
|
set -eu
|
|
# The PS3 RSX driver (gfx/drivers/rsx_gfx.c), compiled in
|
|
# whole against the tree's PSL1GHT stand-ins, with each RSX
|
|
# allocation it makes failed in turn: init, a font and an
|
|
# overlay come up whole or not at all, a texture with no
|
|
# memory is not handed out, a core frame whose texture got
|
|
# none is not drawn, and a frame bigger than its texture's
|
|
# buffer gets a new buffer.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./rsx_alloc_test
|
|
make clean
|
|
|
|
- name: X11 window poll sends no requests (Xvfb, ASan + UBSan)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
# x11_check_window() and x11_has_focus() run every frame; a
|
|
# reply-bearing request there waits behind whatever else is
|
|
# queued on the connection (a software Vulkan WSI's whole
|
|
# PutImage frame). Focus comes from FocusIn/FocusOut, so the
|
|
# X contexts must select FocusChangeMask.
|
|
for f in x_ctx.c x_vk_ctx.c xegl_ctx.c; do
|
|
grep -q FocusChangeMask gfx/drivers_context/$f \
|
|
|| { echo "::error::$f does not select FocusChangeMask"; exit 1; }
|
|
done
|
|
Xvfb :97 -screen 0 1024x768x24 > /tmp/xvfb97.log 2>&1 &
|
|
XVFB_PID=$!
|
|
sleep 1
|
|
(cd samples/gfx/x11_frame_poll \
|
|
&& make clean all SANITIZER=address,undefined \
|
|
&& DISPLAY=:97 ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
timeout 60 ./x11_frame_poll_test)
|
|
kill $XVFB_PID
|
|
echo "[pass] x11_frame_poll_test"
|
|
|
|
- name: Live mode switching on an Xorg dummy server (X11 + SDL)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
# The stubbed display_servers_test proves the driver issues
|
|
# the right RandR calls; this proves a real X server accepts
|
|
# them. Xorg's 'dummy' driver honours XRRCreateMode /
|
|
# XRRAddOutputMode / XRRSetCrtcConfig with arbitrary timings
|
|
# (Xvfb's RandR shim returns success and lists nothing, so it
|
|
# cannot stand in). The X11 sample runs the modeline engine
|
|
# end to end - generate a 15 kHz timing, put it on the wire
|
|
# through dispserv_x11, read the crtc back and compare every
|
|
# field, switch again, restore - then the Screen Resolution
|
|
# callbacks: the listed modes, a smaller, a larger and a
|
|
# rate-only switch with the framebuffer following, an
|
|
# unlisted size refused, and back. The SDL sample switches
|
|
# a real SDL2 window among the listed modes through
|
|
# dispserv_sdl2 (and dispserv_sdl3 on an SDL3 build), for the
|
|
# modeline path and the menu picker.
|
|
# The Win32 build runs after them under Wine.
|
|
sudo Xorg :99 -config samples/gfx/display_servers_x11_live/xorg-dummy.conf \
|
|
-logfile /tmp/xorg-dummy.log -noreset -nolisten tcp -ac \
|
|
> /tmp/xorg-dummy.out 2>&1 &
|
|
XORG_PID=$!
|
|
for i in $(seq 1 30); do
|
|
DISPLAY=:99 xrandr >/dev/null 2>&1 && break
|
|
sleep 0.5
|
|
done
|
|
DISPLAY=:99 xrandr | head -3
|
|
export DISPLAY=:99
|
|
# SDL3's X11 backend makes a window fullscreen through the
|
|
# window manager and reverts the mode switch when none
|
|
# answers; a light one runs for the SDL samples.
|
|
openbox > /tmp/openbox.out 2>&1 &
|
|
sleep 1
|
|
(cd samples/gfx/display_servers_x11_live \
|
|
&& make clean all SANITIZER=address,undefined \
|
|
&& ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
timeout 120 ./display_servers_x11_live_test)
|
|
echo "[pass] display_servers_x11_live_test"
|
|
# The refresh rate a core polls every frame: answered from
|
|
# memory once the X event pump drains the connection, with no
|
|
# request to the server, and changed by the next pump after
|
|
# another client switches the mode - under a reader on a
|
|
# second thread, once under ASan+UBSan and once under TSan.
|
|
(cd samples/gfx/x11_refresh_rate \
|
|
&& make clean all SANITIZER=address,undefined \
|
|
&& ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 120 ./x11_refresh_rate_test \
|
|
&& make clean all SANITIZER=thread \
|
|
&& TSAN_OPTIONS=halt_on_error=1 timeout 120 ./x11_refresh_rate_test)
|
|
echo "[pass] x11_refresh_rate_test"
|
|
(cd samples/gfx/display_servers_sdl2_live \
|
|
&& make clean all SANITIZER=address,undefined \
|
|
&& ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
LSAN_OPTIONS=suppressions=lsan.supp \
|
|
timeout 120 ./display_servers_sdl2_live_test)
|
|
echo "[pass] display_servers_sdl2_live_test"
|
|
# The SDL3 server, audio driver and keyboard against an SDL3
|
|
# built from a pinned release (the runner image ships no
|
|
# libsdl3-dev): video, events and the dummy audio device, X11
|
|
# backend.
|
|
SDL3_VER=3.2.20
|
|
if [ ! -f /opt/sdl3/lib/pkgconfig/sdl3.pc ]; then
|
|
curl -sL -o /tmp/sdl3.tar.gz \
|
|
"https://github.com/libsdl-org/SDL/releases/download/release-$SDL3_VER/SDL3-$SDL3_VER.tar.gz"
|
|
tar -C /tmp -xzf /tmp/sdl3.tar.gz
|
|
cmake -S "/tmp/SDL3-$SDL3_VER" -B /tmp/sdl3-build -G Ninja \
|
|
-DCMAKE_BUILD_TYPE=Release -DSDL_SHARED=ON -DSDL_STATIC=OFF \
|
|
-DSDL_TESTS=OFF -DSDL_EXAMPLES=OFF -DSDL_AUDIO=ON -DSDL_CAMERA=OFF \
|
|
-DSDL_WAYLAND=OFF -DSDL_X11=ON -DSDL_OPENGL=OFF -DSDL_OPENGLES=OFF \
|
|
-DSDL_VULKAN=OFF -DSDL_RENDER=OFF -DCMAKE_INSTALL_PREFIX=/opt/sdl3 \
|
|
> /tmp/sdl3-cmake.log
|
|
ninja -C /tmp/sdl3-build > /tmp/sdl3-build.log
|
|
sudo ninja -C /tmp/sdl3-build install > /tmp/sdl3-install.log
|
|
fi
|
|
# A 59.94 mode beside the desktop's 60, which rounds alike
|
|
xrandr --newmode "1280x720_59.94" 74.176 1280 1390 1430 1650 720 725 730 750 +hsync +vsync
|
|
xrandr --addmode DUMMY0 1280x720_59.94
|
|
(cd samples/gfx/display_servers_sdl3_live \
|
|
&& PKG_CONFIG_PATH=/opt/sdl3/lib/pkgconfig make clean all SANITIZER=address,undefined \
|
|
&& LD_LIBRARY_PATH=/opt/sdl3/lib \
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
LSAN_OPTIONS=suppressions=lsan.supp \
|
|
timeout 120 ./display_servers_sdl3_live_test)
|
|
echo "[pass] display_servers_sdl3_live_test"
|
|
(cd samples/audio/sdl3_counters \
|
|
&& PKG_CONFIG_PATH=/opt/sdl3/lib/pkgconfig make clean all SANITIZER=address,undefined \
|
|
&& LD_LIBRARY_PATH=/opt/sdl3/lib SDL_AUDIO_DRIVER=dummy \
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
LSAN_OPTIONS=suppressions=../../gfx/display_servers_sdl3_live/lsan.supp \
|
|
timeout 120 ./sdl3_counters_test \
|
|
&& make clean)
|
|
echo "[pass] sdl3_counters_test"
|
|
(cd samples/input/sdl3_keyboard \
|
|
&& PKG_CONFIG_PATH=/opt/sdl3/lib/pkgconfig make clean all SANITIZER=address,undefined \
|
|
&& LD_LIBRARY_PATH=/opt/sdl3/lib \
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
LSAN_OPTIONS=suppressions=../../gfx/display_servers_sdl3_live/lsan.supp \
|
|
timeout 120 ./sdl3_keyboard_test \
|
|
&& make clean)
|
|
echo "[pass] sdl3_keyboard_test"
|
|
# The Win32 server, its vendor timing paths and the engine
|
|
# cross-built with mingw-w64 and run under Wine on the same
|
|
# display: Wine backs EnumDisplaySettingsExA and
|
|
# ChangeDisplaySettingsExA with the server's RandR modes, so
|
|
# the CDS listed-mode switch is real; ADL, ATI legacy and
|
|
# PowerStrip are linked and must decline.
|
|
# Its own Wine prefix: one a Wine program started before the
|
|
# display was up keeps that display's single mode, and Wine
|
|
# lists only it.
|
|
WINE_LIVE_PREFIX=$(mktemp -d)
|
|
(cd samples/gfx/display_servers_win32_live \
|
|
&& make clean all \
|
|
&& WINEPREFIX=$WINE_LIVE_PREFIX WINEDEBUG=-all timeout 300 \
|
|
wine ./display_servers_win32_live_test.exe)
|
|
WINEPREFIX=$WINE_LIVE_PREFIX wineserver -k || true
|
|
rm -rf "$WINE_LIVE_PREFIX"
|
|
echo "[pass] display_servers_win32_live_test (Wine)"
|
|
# The vendor timing paths against stand-ins: a mock
|
|
# atiadlxx.dll that re-plugs the monitor after a forced set,
|
|
# a scratch HKLM key for the ATI legacy registry layout, a
|
|
# TPShidden window answering PowerStrip's messages.
|
|
(cd samples/gfx/display_servers_win32_vendor \
|
|
&& make clean all \
|
|
&& WINEDEBUG=-all timeout 300 wine ./display_servers_win32_vendor_test.exe)
|
|
echo "[pass] display_servers_win32_vendor_test (Wine)"
|
|
# The server must be back on its desktop mode with nothing
|
|
# generated left behind.
|
|
xrandr | head -3
|
|
if xrandr | grep -q 'RA-'; then
|
|
echo "generated modes left on the server"; exit 1
|
|
fi
|
|
pkill openbox || true
|
|
sudo kill "$XORG_PID" || true
|
|
|
|
- name: Screen Resolution through Mutter's DisplayConfig (ASan + UBSan, TSan)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
# Under GNOME's Wayland session the X11 driver runs on
|
|
# XWayland, which lists every mode at the desktop's current
|
|
# rate and only scales the window when "switched", and the
|
|
# Wayland driver had no resolution list at all; Mutter's
|
|
# org.gnome.Mutter.DisplayConfig D-Bus interface is the only
|
|
# way to the monitor's real modes. mock_mutter.py carries
|
|
# Mutter's signatures and its ApplyMonitorsConfig checks; the
|
|
# test drives gfx/common/mutter_displayconfig.c directly and
|
|
# through dispserv_x11 (real Xorg dummy server, and Xwayland
|
|
# under a headless Weston) and dispserv_wl, with and without
|
|
# Mutter on the bus. Without it every path must behave
|
|
# exactly as before: no session bus, a bus without Mutter, a
|
|
# real X server (Mutter is never asked), XWayland's own list,
|
|
# and no Wayland resolution list. Last, a real headless Mutter
|
|
# and its XWayland: Mutter names XWayland's outputs after its
|
|
# connectors (DP-1, Meta-0), so only the XWAYLAND extension
|
|
# says what the server is.
|
|
(cd samples/gfx/mutter_displayconfig \
|
|
&& make clean all SANITIZER=address,undefined \
|
|
&& ASAN_OPTIONS=detect_leaks=1 \
|
|
UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
XORG="sudo Xorg" timeout 300 ./run.sh)
|
|
# The worker publishes its state by swapping a pointer and
|
|
# frees the one it replaced once no reader is inside it, with
|
|
# no lock. The threads case reads the list from four threads
|
|
# while the rate is switched back and forth; freeing the old
|
|
# state without waiting for the readers is a TSan data race.
|
|
# The bus cases alone: they need no display server.
|
|
(cd samples/gfx/mutter_displayconfig \
|
|
&& make clean all SANITIZER=thread \
|
|
&& TSAN_OPTIONS=halt_on_error=1 BUS_ONLY=1 \
|
|
setarch "$(uname -m)" -R timeout 120 ./run.sh)
|
|
echo "[pass] mutter_displayconfig_test"
|
|
|
|
- name: Build and run dbus_screensaver_test (ASan + UBSan, TSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/dbus_screensaver
|
|
run: |
|
|
set -eu
|
|
# gfx/common/dbus_common.c's screensaver inhibit against a
|
|
# private session bus and a fake org.freedesktop.ScreenSaver:
|
|
# each request answered as itself and never with an older
|
|
# one's answer, three threads toggling leave what the last one
|
|
# asked, close and reopen release the old inhibit, and no
|
|
# service answers FAILED.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 300 ./run.sh
|
|
make clean SANITIZER=thread && make -j$(nproc) all SANITIZER=thread
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 300 ./run.sh
|
|
echo "[pass] dbus_screensaver_test"
|
|
|
|
- name: Single GL context initialization ownership
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
CC=gcc CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" python3 samples/gfx/threaded_video/gl_context_init.py
|
|
CC=clang CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" python3 samples/gfx/threaded_video/gl_context_init.py
|
|
CC=x86_64-w64-mingw32-gcc RUNNER=wine python3 samples/gfx/threaded_video/gl_context_init.py
|
|
CC=gcc CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" python3 samples/gfx/threaded_video/gl_context_drain.py
|
|
CC=clang CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" python3 samples/gfx/threaded_video/gl_context_drain.py
|
|
CC=x86_64-w64-mingw32-gcc RUNNER=wine python3 samples/gfx/threaded_video/gl_context_drain.py
|
|
|
|
- name: D3D menu source formats (Linux and mingw + Wine)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" python3 samples/gfx/threaded_video/d3d_menu_format.py
|
|
CC=x86_64-w64-mingw32-gcc RUNNER=wine python3 samples/gfx/threaded_video/d3d_menu_format.py
|
|
CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" python3 samples/gfx/threaded_video/d3d12_menu.py
|
|
CC=x86_64-w64-mingw32-gcc RUNNER=wine python3 samples/gfx/threaded_video/d3d12_menu.py
|
|
CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" python3 samples/gfx/threaded_video/hdr_view_crop.py
|
|
CC=x86_64-w64-mingw32-gcc RUNNER=wine python3 samples/gfx/threaded_video/hdr_view_crop.py
|
|
CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" python3 samples/gfx/threaded_video/d3d12_raw_frame.py
|
|
CC=x86_64-w64-mingw32-gcc RUNNER=wine python3 samples/gfx/threaded_video/d3d12_raw_frame.py
|
|
python3 samples/gfx/d3d12_menu_mapping/run.py
|
|
|
|
- name: D3D8 resource cleanup (Linux and mingw + Wine)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
make -C samples/gfx/d3d8_cleanup clean check SANITIZER=address,undefined
|
|
make -C samples/gfx/d3d8_cleanup clean check CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
|
|
make -C samples/gfx/d3d8_cleanup clean
|
|
|
|
- name: D3D11/12 shader feedback lifecycle (Linux and mingw + Wine)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
make -C samples/gfx/d3d_feedback clean check SANITIZER=address,undefined
|
|
make -C samples/gfx/d3d_feedback clean check CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
|
|
make -C samples/gfx/d3d_feedback clean
|
|
|
|
- name: D3D12 mipmap generation (Linux and mingw + Wine)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
make -C samples/gfx/d3d12_mipmaps clean check SANITIZER=address,undefined
|
|
make -C samples/gfx/d3d12_mipmaps clean check CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
|
|
make -C samples/gfx/d3d12_mipmaps clean
|
|
|
|
- name: D3D11 video decode backend (Linux and mingw + Wine)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
# H.264, HEVC and VP9 submission buffers, byte by byte
|
|
# and against dxva.h, and the backend itself on a D3D11 that
|
|
# records what it is told. The GPU oracle builds here and
|
|
# skips; it runs on a Windows machine with a video decoder.
|
|
make -C samples/gfx/d3d11_video_decode clean check SANITIZER=address,undefined
|
|
make -C samples/gfx/d3d11_video_decode clean check CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
|
|
make -C samples/gfx/d3d11_video_decode clean
|
|
|
|
- name: Movie GPU handoff (Linux and mingw + Wine)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
CFLAGS="-O1 -g -std=c89 -Wall -Wextra -fsanitize=address,undefined -fno-omit-frame-pointer" python3 samples/gfx/movie_gpu_handoff/run.py
|
|
CFLAGS="-O1 -g -std=c89 -Wall -Wextra -fsanitize=thread -fno-omit-frame-pointer" python3 samples/gfx/movie_gpu_handoff/run.py
|
|
CC=x86_64-w64-mingw32-gcc RUNNER=wine python3 samples/gfx/movie_gpu_handoff/run.py
|
|
|
|
- name: D3D12 descriptor cache resource lifetime (Linux and mingw + Wine)
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
CFLAGS="-O1 -g -std=c89 -Wall -Wextra -fsanitize=address,undefined -fno-omit-frame-pointer" python3 samples/gfx/d3d12_descriptor_cache/run.py
|
|
CC=x86_64-w64-mingw32-gcc RUNNER=wine python3 samples/gfx/d3d12_descriptor_cache/run.py
|
|
|
|
- name: D3D12 pass descriptor tables on their own slots (Linux and mingw + Wine)
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
CFLAGS="-O1 -g -std=c89 -Wall -Wextra -fsanitize=address,undefined -fno-omit-frame-pointer" python3 samples/gfx/d3d12_pass_tables/run.py
|
|
CC=x86_64-w64-mingw32-gcc RUNNER=wine python3 samples/gfx/d3d12_pass_tables/run.py
|
|
|
|
- name: D3D12 repeated screenshots and recording slots (Linux and mingw + Wine)
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
CFLAGS="-O1 -g -fsanitize=address,undefined -fno-omit-frame-pointer" python3 samples/gfx/d3d12_readback_slot/run.py
|
|
CC=x86_64-w64-mingw32-gcc RUNNER=wine python3 samples/gfx/d3d12_readback_slot/run.py
|
|
|
|
- name: D3D12 frame ring, one frame in flight or two (Linux and mingw + Wine)
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
CFLAGS="-O1 -g -std=c89 -Wall -Wextra -fsanitize=address,undefined -fno-omit-frame-pointer" python3 samples/gfx/d3d12_frame_ring/run.py
|
|
CC=x86_64-w64-mingw32-gcc RUNNER=wine python3 samples/gfx/d3d12_frame_ring/run.py
|
|
|
|
- name: D3D10/11 failed maps and swapchain recovery (Linux and mingw + Wine)
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
CFLAGS="-O1 -g -std=c89 -Wall -Wextra -fsanitize=address,undefined -fno-omit-frame-pointer" python3 samples/gfx/d3d_failure_paths/run.py
|
|
CC=x86_64-w64-mingw32-gcc RUNNER=wine python3 samples/gfx/d3d_failure_paths/run.py
|
|
CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" python3 samples/gfx/d3d_failure_paths/upload.py
|
|
CC=x86_64-w64-mingw32-gcc RUNNER=wine python3 samples/gfx/d3d_failure_paths/upload.py
|
|
python3 samples/gfx/d3d_failure_paths/gpu.py
|
|
CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" python3 samples/gfx/d3d_failure_paths/lend.py
|
|
CC=x86_64-w64-mingw32-gcc RUNNER=wine python3 samples/gfx/d3d_failure_paths/lend.py
|
|
python3 samples/gfx/d3d_failure_paths/lend_gpu.py
|
|
|
|
- name: D3D9 HLSL failed locks (Linux and mingw + Wine)
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
CFLAGS="-O1 -g -std=c89 -Wall -Wextra -fsanitize=address,undefined -fno-omit-frame-pointer" python3 samples/gfx/d3d9_failed_lock/run.py
|
|
CC=x86_64-w64-mingw32-gcc RUNNER=wine python3 samples/gfx/d3d9_failed_lock/run.py
|
|
|
|
- name: Vulkan video negotiation and HEVC parameters (Linux and mingw + Wine)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
make -C samples/gfx/vulkan_video_caps clean check SANITIZER=address,undefined
|
|
make -C samples/gfx/vulkan_video_caps clean check CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
|
|
make -C samples/gfx/vulkan_video_caps clean
|
|
make -C samples/gfx/vulkan_video_caps av1-session-check
|
|
make -C samples/gfx/vulkan_video_caps av1-session-check CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
|
|
make -C samples/gfx/vulkan_video_caps session-check
|
|
make -C samples/gfx/vulkan_video_caps session-check CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
|
|
make -C samples/gfx/vulkan_video_caps decode-check
|
|
make -C samples/gfx/vulkan_video_caps decode-check CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
|
|
make -C samples/gfx/vulkan_video_caps import-check
|
|
make -C samples/gfx/vulkan_video_caps import-check CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
|
|
make -C samples/gfx/vulkan_video_caps render-check
|
|
make -C samples/gfx/vulkan_video_caps render-check CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
|
|
make -C samples/gfx/vulkan_video_caps direct-check
|
|
make -C samples/gfx/vulkan_video_caps direct-check CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
|
|
make -C samples/gfx/vulkan_video_caps clean
|
|
|
|
- name: GPU recording slot ownership (Linux, mingw and sanitizers)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
make -C samples/gfx/record_gpu_queue clean check
|
|
make -C samples/gfx/record_gpu_queue clean check THREADS=1 SANITIZE=-fsanitize=address,undefined
|
|
make -C samples/gfx/record_gpu_queue clean check THREADS=1 SANITIZE=-fsanitize=thread
|
|
make -C samples/gfx/record_gpu_queue clean check THREADS=1 TEST_FRAMES=20000 CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
|
|
|
|
- name: Native NVENC D3D12 encoder (portable fallback and hardware gate)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
make -C samples/gfx/nvenc_d3d12 check
|
|
make -C samples/gfx/nvenc_d3d12 check CC=x86_64-w64-mingw32-gcc EXE=.exe RUNNER=wine
|
|
|
|
- name: D3D11 hardware ring under threaded video (mingw + Wine, headless)
|
|
shell: bash
|
|
run: |
|
|
set -o pipefail
|
|
# The threaded wrapper hands a D3D11 hardware core a deferred
|
|
# context and replays its frames on the video thread. Nothing
|
|
# on Linux exercises that, and it shipped showing one frame
|
|
# and then black. The real proxy and the driver's own
|
|
# FinishCommandList argument, run under Wine, where wined3d
|
|
# implements deferred contexts over OpenGL. Requires the ring
|
|
# and the immediate context to show every frame, and the ring
|
|
# as it used to be to fail.
|
|
samples/gfx/d3d11_hw_ring/run.sh
|
|
|
|
- name: libretro_d3d11.h version 2, the contract run (mingw + Wine, headless)
|
|
shell: bash
|
|
run: |
|
|
set -o pipefail
|
|
# Version 2 of the D3D11 hardware render interface lets a core
|
|
# and a frontend that presents from another thread share the
|
|
# device's one immediate context by taking turns, so the core
|
|
# keeps everything a deferred context cannot do - reading back
|
|
# above all. This runs the two against each other on a real
|
|
# D3D11 device (wined3d), and requires every frame and every
|
|
# readback to be right, and a core that ignores what
|
|
# lock_context tells it to be caught.
|
|
REQUIRE_D3D11=1 samples/gfx/d3d11_hw_interface_v2/run.sh
|
|
|
|
- name: libretro_d3d12.h version 2, the contract run (mingw + Wine + vkd3d, headless)
|
|
shell: bash
|
|
run: |
|
|
set -o pipefail
|
|
# Version 2 of the D3D12 hardware render interface lets a core
|
|
# hand a frame to a frontend that presents from another thread
|
|
# without the copy version 1 forces. This runs a core and such a
|
|
# frontend against each other on a real D3D12 device - vkd3d
|
|
# over Mesa's software Vulkan driver - and requires every frame
|
|
# to arrive intact, and a core that ignores wait_sync_index to
|
|
# be caught.
|
|
REQUIRE_D3D12=1 samples/gfx/d3d12_hw_interface_v2/run.sh
|
|
|
|
- name: D3D texture pokes marshal onto the video thread (mingw, object check)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
# Every texture poke a caller reaches with threaded=true that
|
|
# touches a device's queue or descriptor heaps goes through
|
|
# video_thread_texture_handle(); read off the compiled object's
|
|
# relocations, D3D12's compressed load included.
|
|
make -C samples/gfx/d3d_texture_marshal check
|
|
|
|
- name: Win32 window procedure on a real window (mingw + Wine, headless)
|
|
shell: bash
|
|
run: |
|
|
set -o pipefail
|
|
# gfx/common/win32_common.c had twelve window procedures and
|
|
# has one. This links the real file with stand-ins for what
|
|
# it calls, has it create its window for every video family
|
|
# and kind of input driver, sends each a script of messages,
|
|
# and compares every answer and every call made on the way
|
|
# with the trace the twelve old procedures gave on the same
|
|
# script - then makes and destroys the window through twenty
|
|
# fullscreen toggles. Any difference from the recorded trace
|
|
# fails.
|
|
samples/gfx/win32_window_proc/run.sh
|
|
python3 samples/gfx/win32_window_proc/modal_frame_run.py
|
|
|
|
- name: Build and run window_driver_test for Windows (Wine)
|
|
shell: bash
|
|
working-directory: samples/input/window_driver
|
|
run: |
|
|
set -eu
|
|
# Which input driver a Windows window gets
|
|
# (input_driver_init_windows() in
|
|
# input/input_driver_choice.c): raw input when it is the
|
|
# setting and it starts, DirectInput otherwise, including
|
|
# when raw input fails to start. Built with mingw-w64 and
|
|
# run under Wine, with stand-in drivers. The X11 and
|
|
# no-window-system choices run on the host in the input
|
|
# samples workflow.
|
|
make clean check-windows
|
|
make clean
|
|
|
|
- name: Build and run display_servers_kms_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/display_servers_kms
|
|
run: |
|
|
set -eu
|
|
# Regression test for two defects in
|
|
# gfx/display_servers/dispserv_kms.c::
|
|
# kms_display_server_get_resolution_list(). g_drm_connector
|
|
# is owned by gfx/common/drm_common.c and set to NULL by
|
|
# drm_free(), which runs on every context teardown -- a
|
|
# resolution change, a fullscreen toggle, a driver reinit --
|
|
# while the display server object outlives it, so the menu
|
|
# could ask for the list with no connector to describe and
|
|
# the function read straight through NULL. g_drm_mode, the
|
|
# sibling global, was already tested for exactly this a few
|
|
# lines earlier. Separately the qsort() at the end was
|
|
# passed `count`, which was declared zero and never
|
|
# assigned; `j` is the element count. So the sort ran over
|
|
# zero elements and the resolution list came back in
|
|
# whatever order the connector reported its modes -- no
|
|
# crash, no diagnostic, just an unsorted list that looks
|
|
# plausible.
|
|
#
|
|
# The test fabricates the drmModeConnector the driver reads,
|
|
# so it needs the libdrm headers but links nothing from
|
|
# libdrm: presenting a connector that is absent, empty, or
|
|
# reporting modes in an awkward order is the whole exercise
|
|
# and a real one will not do it on request. The ordering
|
|
# check feeds modes in descending size, which a working sort
|
|
# must reverse and a broken one leaves untouched. If
|
|
# dispserv_kms.c amends either, this goes red.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
test -x display_servers_kms_test
|
|
timeout 60 ./display_servers_kms_test
|
|
echo "[pass] display_servers_kms_test"
|
|
|
|
- name: Build and run drm_hdr_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/drm_hdr
|
|
run: |
|
|
set -eu
|
|
# gfx/common/drm_hdr.c: which EDIDs take HDR10 and what they
|
|
# declare, malformed ones refused without an overread, and the
|
|
# HDR_OUTPUT_METADATA blob, whose layout must be the kernel's.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./drm_hdr_test
|
|
echo "[pass] drm_hdr_test"
|
|
|
|
- name: Build and run drm_surface_lifetime_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/drm_surface_lifetime
|
|
run: |
|
|
set -eu
|
|
# gfx/drivers/drm_gfx.c's surfaces against a fake kernel: a
|
|
# core's resolution changes and the menu's sessions leave only
|
|
# the live surface's dumb buffers, framebuffers and mappings,
|
|
# the framebuffer the plane scans out is never removed from
|
|
# under it, and the driver's free leaves nothing - LeakSanitizer
|
|
# holds the plane queries' libdrm objects to the same. Rows the
|
|
# kernel pads are written at its stride, each frame read at its
|
|
# own pitch, the next page lent to the core and flipped without
|
|
# a copy, a dupe survived, and no allocation made per flip. The
|
|
# menu scans out RGUI's RGBA4444 as it is where the plane takes
|
|
# it, is lent to RGUI, follows the frame's size, and asks the
|
|
# plane once rather than every menu frame. Every framebuffer is
|
|
# made in its surface's own format, which no legacy depth names
|
|
# for RGBX4444. The input overlay goes on a plane of its own,
|
|
# raised above the frame's and blending premultiplied, covering
|
|
# only its images, composed when something changes and flipped
|
|
# in the frame's own commit; where no plane can take it, it is
|
|
# blended into the frame. Init passes over a card with no
|
|
# display, keeps the console's mode or a fullscreen size's,
|
|
# switches mode with the planes, flips without atomic commits
|
|
# where the card has none, fails cleanly with no plane for
|
|
# frames, and the free turns the plane off and gives the
|
|
# console back its framebuffer and mode. A core's rotation is
|
|
# made by the plane where its rotation property takes it, and
|
|
# the frame still lent; otherwise, or under a blended overlay,
|
|
# the copy turns it and nothing is lent. The menu is shown
|
|
# unturned, the plane is left unturned, and a screenshot reads
|
|
# the frame back as it is shown. Without vsync, commits do not
|
|
# wait: one per vblank, none refused, a frame that comes before
|
|
# the last is on screen written over, the page written next
|
|
# never the one on screen or on its way; a resize, the menu, a
|
|
# mode switch and the free wait for the flip first, and vsync
|
|
# turned back on waits again. The threaded wrapper's frame
|
|
# memory is scanned out where a frame lies, from inside a
|
|
# buffer too, never written while shown or on its way, freed
|
|
# only once the plane has left it, and copied where the display
|
|
# refuses the pitch or the copy turns the frame. The CRT
|
|
# switch's modeline is set at init and on a mode switch, HDMI
|
|
# timings from the config, malformed ones passed over, and the
|
|
# aspect the switch puts taken up with the next frame; content
|
|
# running under the menu is never flipped over it, and its last
|
|
# frame goes up when the menu closes. On-screen messages are
|
|
# drawn into the frame where they are seen, at the size the
|
|
# screen shows, upright on a frame the plane turns, and a frame
|
|
# in the wrapper's memory is copied to be drawn on. A blended
|
|
# overlay is blended into a core's views as into a frame. The
|
|
# driver is built with sign-compare as an error and linked
|
|
# without rthreads, so it stays clean of both. A turn goes in
|
|
# the commit that places the plane, never on its own, and a
|
|
# frame placed otherwise in a frame memory buffer still shown
|
|
# is copied rather than its framebuffer removed.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./drm_surface_lifetime_test
|
|
make clean
|
|
echo "[pass] drm_surface_lifetime_test"
|
|
|
|
- name: Build and run drm_crtc_pick_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/drm_crtc_pick
|
|
run: |
|
|
set -eu
|
|
# gfx/common/drm_common.c against a fake kernel: a connector
|
|
# nothing drives yet (a display plugged in after boot) gets an
|
|
# encoder and a CRTC that is off, a CRTC that was off is
|
|
# switched off again on restore, a size the display lacks falls
|
|
# back to mode 0, only a display in use going away (or
|
|
# coming back) restarts the video driver on a hotplug, and the
|
|
# flip wait notes a hotplug in its own poll without spinning on
|
|
# the unread socket.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./drm_crtc_pick_test
|
|
make clean
|
|
echo "[pass] drm_crtc_pick_test"
|
|
|
|
- name: Build and run exynos_g2d_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/exynos_g2d
|
|
run: |
|
|
set -eu
|
|
# gfx/drivers/exynos_gfx.c against a fake kernel and a G2D that
|
|
# draws into the GEM buffers: the frame at its aspect ratio, a
|
|
# core rendering into the buffer the G2D reads, vsync off
|
|
# neither waiting nor queueing flips, messages and statistics
|
|
# cleared and blended over only what they cover, the menu's
|
|
# alpha, screenshots, mode switches and CRT modelines, and a
|
|
# free and a failed init that leave nothing behind. Behind an
|
|
# IOMMU, frames read from the core's own memory - the kernel
|
|
# reuses a mapping unsynced, so a frame must never come
|
|
# through one - and the input overlay blended by the G2D,
|
|
# mirrored where its quad is; a core's rotation, turned as
|
|
# the frame is copied.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./exynos_g2d_test
|
|
make clean
|
|
echo "[pass] exynos_g2d_test"
|
|
|
|
- name: Build and run egl_gbm_frames_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/egl_gbm_frames
|
|
run: |
|
|
set -eu
|
|
# gfx/common/egl_gbm_frames.c against a fake GBM, EGL and GPU:
|
|
# a format the GPU samples where the CPU writes it is taken, one
|
|
# mapped through staging memory refused for the rest of the run;
|
|
# a buffer a texture holds, one a draw may still read and the
|
|
# last frame's are never lent; the check puts the GL state back,
|
|
# and the free leaves no buffer, map, image, fence or fd.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./egl_gbm_frames_test
|
|
make clean
|
|
echo "[pass] egl_gbm_frames_test"
|
|
|
|
- name: egl_gbm_frames check on a real GL (Mesa EGL, GLES 2 and desktop GL)
|
|
shell: bash
|
|
run: |
|
|
set -eu
|
|
# The check gfx/common/egl_gbm_frames.c runs before a format is
|
|
# first used, on llvmpipe: GBM and the dma-buf import are
|
|
# stand-ins, the shader, target, draw and readback the GL's own.
|
|
# A buffer sampled as written passes in both formats; one whose
|
|
# writes are never seen fails, and is not tried again.
|
|
T=$(mktemp -d)
|
|
for api in es2 gl; do
|
|
if [ $api = es2 ]; then
|
|
D="-DHAVE_OPENGLES -DHAVE_OPENGLES2"
|
|
L="libretro-common/glsym/glsym_es2.c -lEGL -lGLESv2"
|
|
else
|
|
D="-DHAVE_OPENGL"
|
|
L="libretro-common/glsym/glsym_gl.c -lEGL -lGL"
|
|
fi
|
|
cc -std=gnu89 -O1 -g1 -Wall -Werror=implicit-function-declaration \
|
|
-fsanitize=address,undefined -fno-omit-frame-pointer \
|
|
-DHAVE_KMS -DHAVE_EGL $D -I. -Ilibretro-common/include \
|
|
-Igfx/include $(pkg-config --cflags libdrm) \
|
|
samples/gfx/egl_gbm_frames/mesa_check.c gfx/common/egl_gbm_frames.c \
|
|
libretro-common/glsym/rglgen.c $L \
|
|
-Wl,--wrap=eglGetProcAddress,--wrap=eglQueryString,--wrap=glDrawArrays \
|
|
-o "$T/check_$api"
|
|
for mode in seen blind; do
|
|
EGL_PLATFORM=surfaceless LIBGL_ALWAYS_SOFTWARE=1 \
|
|
ASAN_OPTIONS=detect_leaks=0 UBSAN_OPTIONS=halt_on_error=1 \
|
|
"$T/check_$api" $mode
|
|
done
|
|
done
|
|
rm -rf "$T"
|
|
|
|
- name: Build and run gl2_kms_frames_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gl2_kms_frames
|
|
run: |
|
|
set -eu
|
|
# gfx/drivers/gl2.c's frames in scanout memory against a
|
|
# scripted egl_gbm_frames: on KMS a plain software frame is lent
|
|
# from the buffers, never to a core that reads it back, a
|
|
# hardware core, mipmaps or a 10-bit source; a lent frame becomes
|
|
# the frame texture's storage with no upload, and any other gets
|
|
# the texture's own storage back before it goes up.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./gl2_kms_frames_test
|
|
make clean
|
|
echo "[pass] gl2_kms_frames_test"
|
|
|
|
- name: Build and run wayland_wlr_output_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/wayland_wlr_output
|
|
run: |
|
|
set -eu
|
|
# gfx/common/wayland_wlr_output.c, the Wayland display server's
|
|
# resolution list and mode switch on wlroots compositors, against
|
|
# an in-process compositor that checks every configuration names
|
|
# each head exactly once, as the protocol requires.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./wayland_wlr_output_test
|
|
echo "[pass] wayland_wlr_output_test"
|
|
|
|
- name: Build and run wayland_cursor_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/wayland_cursor
|
|
run: |
|
|
set -eu
|
|
# input/common/wayland_cursor.h: the size and scale the Wayland
|
|
# context loads its cursor theme at when the compositor has no
|
|
# cursor-shape-v1 - XCURSOR_SIZE honoured, fractional scales
|
|
# rounded up to a whole buffer scale.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./wayland_cursor_test
|
|
echo "[pass] wayland_cursor_test"
|
|
|
|
- name: Build and run wayland_resize_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/wayland_resize
|
|
run: |
|
|
set -eu
|
|
# gfx/common/wayland_resize.h, what the Wayland GL and Vulkan
|
|
# contexts do once their buffers are resized, against an
|
|
# in-process compositor: configures count again whatever the
|
|
# scaling, and the buffer scale goes only to a surface on the
|
|
# whole-scale path that is new enough to take it.
|
|
export XDG_RUNTIME_DIR=$(mktemp -d)
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./wayland_resize_test
|
|
echo "[pass] wayland_resize_test"
|
|
|
|
- name: Build and run video_mode_select_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/video_mode_select
|
|
run: |
|
|
set -eu
|
|
# gfx/common/video_mode_select.c: which listed mode a size and
|
|
# rate name, and the order a resolution list is shown in - the
|
|
# code every display server's mode switch goes through.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./video_mode_select_test
|
|
echo "[pass] video_mode_select_test"
|
|
|
|
- name: Build and run wayland_kwin_output_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/wayland_kwin_output
|
|
run: |
|
|
set -eu
|
|
# gfx/common/wayland_kwin_output.c, the Wayland display server's
|
|
# resolution list and mode switch on KWin, against an in-process
|
|
# compositor speaking KWin's output protocols in both the
|
|
# registry form and the older per-output globals.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./wayland_kwin_output_test
|
|
echo "[pass] wayland_kwin_output_test"
|
|
|
|
- name: Build and run wayland_color_management_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/wayland_color_management
|
|
run: |
|
|
set -eu
|
|
# gfx/common/wayland_color.c, which tags an HDR GL surface as
|
|
# Windows-scRGB, against an in-process compositor that records
|
|
# every fatal wp_color_manager_v1 rule the client could break.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./wayland_color_management_test
|
|
echo "[pass] wayland_color_management_test"
|
|
|
|
- name: Build and run wayland_present_test (TSan, ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/wayland_present
|
|
run: |
|
|
set -eu
|
|
# gfx/common/wayland_present.c against an in-process
|
|
# compositor: presentation feedback and the GL frame callback
|
|
# live on their own event queues, so the main thread
|
|
# dispatching input never runs them while the video thread
|
|
# requests, waits on and frees them.
|
|
# TSan catches a feedback event dispatched off that thread;
|
|
# ASan the feedback left behind at teardown.
|
|
make clean SANITIZER=thread && make -j$(nproc) all SANITIZER=thread
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 60 ./wayland_present_test
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./wayland_present_test
|
|
echo "[pass] wayland_present_test"
|
|
|
|
- name: Build and run wayland_drm_lease_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/wayland_drm_lease
|
|
run: |
|
|
set -eu
|
|
# The DRM lease path in gfx/common/wayland_drm_lease.c and
|
|
# gfx/display_servers/dispserv_wl.c, driven by a test
|
|
# compositor on libwayland-server: what is offered, which
|
|
# connector an index picks, an index past the offer, a
|
|
# refusal, a revoke arriving while the client is idle, and
|
|
# no Wayland session at all. The leased descriptor and the
|
|
# device's non-master fd are the ones a mistake leaks, so
|
|
# the lane runs under ASan. Both protocol halves are
|
|
# generated here from deps/wayland-protocols, which needs
|
|
# wayland-scanner and the client and server libraries -
|
|
# libwayland-dev carries all three.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
test -x wayland_drm_lease_test
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
timeout 60 ./wayland_drm_lease_test
|
|
echo "[pass] wayland_drm_lease_test"
|
|
|
|
- name: Build and run modeline_edid_parse_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/modeline_edid_parse
|
|
run: |
|
|
set -eu
|
|
# The EDID reader in gfx/modeline/modeline_edid.c, fed the
|
|
# block the writer produces (round trip), a hand-built EDID
|
|
# 1.4 with CTA-861 and DisplayID 2.0 extensions checked
|
|
# field by field, the defect cases (short, wrong header, bad
|
|
# checksums, a declared but missing extension, data block
|
|
# and section lengths running past their block), and a
|
|
# deterministic byte-noise sweep. The parser is handed
|
|
# whatever an OS reader returns, so every length and offset
|
|
# in the data is hostile; under ASan a read past the buffer
|
|
# is the failure this lane exists to catch.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
test -x modeline_edid_parse_test
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
timeout 60 ./modeline_edid_parse_test
|
|
|
|
- name: Build and run integer_scale_axis_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/integer_scale_axis
|
|
run: |
|
|
set -eu
|
|
# video_viewport_get_scaled_integer(), pulled out of
|
|
# gfx/video_driver.c by extract_integer.awk, swept over
|
|
# viewport sizes, frame sizes, every axis mode that touches
|
|
# X and every scaling mode. Y+X has to be integer on both
|
|
# axes: the X axis was once multiplied from the
|
|
# aspect-corrected width (256x224 at 64:49 is 293), giving
|
|
# 1172x896 at 1080p - a 4.58x horizontal scale and uneven
|
|
# pixel columns - on every video driver.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
test -x integer_scale_axis_test
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
timeout 60 ./integer_scale_axis_test
|
|
make clean
|
|
|
|
- name: Build and run coord_array_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/coord_array
|
|
run: |
|
|
set -eu
|
|
# The coordinate array, pulled out of gfx/video_driver.c by
|
|
# extract_coord_array.awk: appends hold what was appended,
|
|
# and an append onto an empty array or one that has to grow,
|
|
# with each of its allocations failed in turn, reports the
|
|
# failure, keeps what the array held, and frees clean.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1:halt_on_error=1 \
|
|
timeout 60 ./coord_array_test
|
|
make clean
|
|
|
|
- name: Build and run slang_cache_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/slang_cache
|
|
run: |
|
|
set -eu
|
|
# The SPIR-V cache: slang_cache.c serialises entries in
|
|
# memory and video_shader_parse.c stores them - mapped or read
|
|
# in one go, written whole by rename. The test round-trips an
|
|
# entry, checks the bytes are the layout caches have always
|
|
# had, turns away every truncated prefix and a set of corrupt
|
|
# entries, and keeps keys from naming anything outside the
|
|
# cache. Built mapped (HAVE_MMAP) and reading.
|
|
make clean SAN=address,undefined && make -j$(nproc) all SAN=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=halt_on_error=1:print_stacktrace=1 \
|
|
timeout 300 make check
|
|
make clean
|
|
|
|
- name: Build and run gfx_display_mesh_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gfx_display_mesh
|
|
run: |
|
|
set -eu
|
|
# The menu mesh facility in gfx_display.c against a display
|
|
# driver that records what it is handed: mesh creation and
|
|
# its refusals, the CPU path a plain program takes on drivers
|
|
# that do not draw meshes (the transform, the perspective
|
|
# divide, triangles joined into one strip, triangles behind
|
|
# the eye dropped, the tint), each effect program reaching the
|
|
# driver's pipeline under its id, and nothing handed to a
|
|
# driver that takes no strips.
|
|
make clean && make -j$(nproc) all
|
|
timeout 120 make check
|
|
make clean
|
|
|
|
- name: Build and run font_lifecycle_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/font_lifecycle
|
|
run: |
|
|
set -eu
|
|
# Where a font is created follows the threading hint; where
|
|
# it is REBUILT does not - a rebuild marshals whenever the
|
|
# wrapper is up, because a GL backend making its context
|
|
# current on the main thread while the video thread holds it
|
|
# is an X BadAccess. Nothing ran this sample, and it had the
|
|
# older rule written into it.
|
|
# The fallback tests read DejaVu in place of the assets'
|
|
# fallback fonts: Sans for fallback-font.ttf, and Sans Mono
|
|
# and Sans as a general and a second face for the chain test.
|
|
# vg and sdl2 draw on-screen messages through the shipping
|
|
# OpenVG and SDL2 drivers - OpenVG onto a software OpenVG in
|
|
# the test, SDL2 through its own software renderer - and check
|
|
# every glyph texel lands where it should.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1:allocator_may_return_null=1 \
|
|
UBSAN_OPTIONS=print_stacktrace=1 timeout 120 make check SANITIZER=address,undefined
|
|
make clean
|
|
# A fallback font is read on a thread of its own while two
|
|
# fonts on two threads ask for it, and a second face behind
|
|
# the first.
|
|
make fallback chain SAN=thread
|
|
TSAN_OPTIONS=halt_on_error=1 setarch "$(uname -m)" -R \
|
|
timeout 120 ./fallback
|
|
TSAN_OPTIONS=halt_on_error=1 setarch "$(uname -m)" -R \
|
|
timeout 120 ./chain
|
|
make clean
|
|
|
|
- name: Build and run overlay_apng_test (ASan + UBSan, TSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/overlay_apng
|
|
run: |
|
|
set -eu
|
|
# An animated overlay image is a surface with slots whose
|
|
# texture is updated in place, so the pages' handles stand
|
|
# while its frames advance. The sample builds its own APNG
|
|
# fixture and holds that contract: distinct frames, a pass
|
|
# that ends and replays, one texture across the submits, and
|
|
# a free with a frame still in flight.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1:allocator_may_return_null=1 \
|
|
UBSAN_OPTIONS=print_stacktrace=1 timeout 120 ./overlay_apng_test fixtures/anim.png
|
|
make clean SANITIZER=thread && make -j$(nproc) all SANITIZER=thread
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 120 ./overlay_apng_test fixtures/anim.png
|
|
make clean
|
|
|
|
- name: Build and run gfx_thumbnail_anim_test (ASan + UBSan, TSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gfx_thumbnail_anim
|
|
run: |
|
|
set -eu
|
|
# Regression test for the two faults that froze animated
|
|
# thumbnails in the file browser, in gfx/gfx_thumbnail.c.
|
|
#
|
|
# gfx_thumbnail_request_file malloc'd its tag and never set
|
|
# tag->path, so gfx_thumbnail_anim_open - which
|
|
# gfx_thumbnail_handle_upload feeds that field for every
|
|
# load with no video stream to adopt - read uninitialised
|
|
# heap and returned without touching the file. Undefined
|
|
# behaviour rather than a stable failure: a freed tag is
|
|
# the size of the next one, so the allocator commonly
|
|
# handed back a block still holding the previous entry's
|
|
# path, which opened an animation and looked like the
|
|
# feature working. gfx_thumbnail_request fills the field
|
|
# for playlist entries, which is why playlists animated and
|
|
# the browser did not.
|
|
#
|
|
# Separately, anim_install marked windowed animations
|
|
# 'read pending' via !data_transfer_complete(). A window
|
|
# never completes - done stays clear for its whole life -
|
|
# and the pump animate() uses (iterate_while) declines
|
|
# windows by design, so pending forced the pump branch, the
|
|
# pump was a no-op, and animate() returned before ever
|
|
# advancing a frame, every vsync, forever. That is every
|
|
# path-based open: animated WEBP, APNG, and any WEBM/MP4
|
|
# with no still stream to adopt.
|
|
#
|
|
# The oracle links the real gfx_thumbnail.c and measures
|
|
# animation as DISTINCT FRAMES REACHING THE TEXTURE - the
|
|
# video-driver stub CRCs every upload - not as internal
|
|
# flags, which is what every earlier test in this area got
|
|
# wrong and why neither fault was caught. A 188-byte
|
|
# 3-frame lossless WEBP is embedded, so there are no
|
|
# fixtures. One lane drives anim_open with a garbage path
|
|
# and requires no stream; one opens the real animation and
|
|
# requires multiple distinct uploads within bounded paced
|
|
# vsyncs, naming the windowed+pending deadlock explicitly
|
|
# before the timeout would. Both pre-fix trees score zero
|
|
# uploads in 240 vsyncs. If gfx_thumbnail.c amends the
|
|
# pending decision or the request plumbing, this goes red.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
test -x gfx_thumbnail_anim_test
|
|
ASAN_OPTIONS=detect_leaks=1 timeout 120 \
|
|
./gfx_thumbnail_anim_test
|
|
echo "[pass] gfx_thumbnail_anim_test (ASan)"
|
|
make clean SANITIZER=thread && make -j$(nproc) all SANITIZER=thread
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 120 \
|
|
./gfx_thumbnail_anim_test
|
|
echo "[pass] gfx_thumbnail_anim_test (TSan)"
|
|
|
|
- name: Build gfx_thumbnail_anim_test with mingw-w64
|
|
shell: bash
|
|
working-directory: samples/gfx/gfx_thumbnail_anim
|
|
run: |
|
|
set -eu
|
|
# Regression tests build for Windows too.
|
|
make clean
|
|
make CC=x86_64-w64-mingw32-gcc TARGET=gfx_thumbnail_anim_test.exe
|
|
test -f gfx_thumbnail_anim_test.exe
|
|
make clean TARGET=gfx_thumbnail_anim_test.exe
|
|
|
|
- name: Build and run gfx_surface_still_test (ASan + UBSan, mingw-w64)
|
|
shell: bash
|
|
working-directory: samples/gfx/gfx_surface_still
|
|
run: |
|
|
set -eu
|
|
. "$GITHUB_WORKSPACE/.github/workflows/scripts/wine-server.sh"
|
|
# A still keeps the texture up until the new one has landed,
|
|
# updates a same-size image in place, and is freed by the
|
|
# completion of an upload or decode still out when it goes.
|
|
make clean
|
|
make EXTRA_CFLAGS="-fsanitize=address,undefined" LDFLAGS="-fsanitize=address,undefined"
|
|
make check
|
|
make clean
|
|
make CC=x86_64-w64-mingw32-gcc TARGET=gfx_surface_still_test.exe
|
|
test -f gfx_surface_still_test.exe
|
|
make clean TARGET=gfx_surface_still_test.exe
|
|
CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" python3 two_frame.py
|
|
CC=x86_64-w64-mingw32-gcc RUNNER=wine python3 two_frame.py
|
|
CFLAGS="-fsanitize=address,undefined -fno-omit-frame-pointer" python3 remap.py
|
|
CC=x86_64-w64-mingw32-gcc RUNNER=wine python3 remap.py
|
|
|
|
- name: Build and run badge_cache_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/cheevos/badge_cache
|
|
run: |
|
|
set -eu
|
|
# rcheevos_get_badge_texture() never reads, decodes or
|
|
# uploads on the calling thread, so the achievement widgets
|
|
# can ask from the video thread. The test links the real
|
|
# cheevos/cheevos_badge.c with the task and upload stubbed
|
|
# as steppable queues and checks: nothing synchronous on
|
|
# the first ask, one decode per badge, the upload posted
|
|
# only from the decode's completion, the handle handed out
|
|
# exactly once and owned by the taker, locked and unlocked
|
|
# as separate keys, a missing file requesting a download
|
|
# only when asked and loading when the download lands, a
|
|
# failed load not retried per ask, a reset unloading ready
|
|
# handles and orphaning in-flight ones (their late delivery
|
|
# unloaded, never handed to whatever reused the slot), an
|
|
# off-main-thread ask only recorded until the main thread's
|
|
# service starts it, the default badge lent rather than
|
|
# given, a 0 reported as a short wait only while a file on
|
|
# disk is loading, and a refused async upload treated as a
|
|
# failure.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
test -x badge_cache_test
|
|
ASAN_OPTIONS=detect_leaks=1 timeout 60 ./badge_cache_test
|
|
echo "[pass] badge_cache_test (ASan)"
|
|
make clean
|
|
|
|
- name: Build and run progress_bounds_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/cheevos/progress_bounds
|
|
run: |
|
|
set -eu
|
|
# Achievement progress comes back from a save state or a
|
|
# netplay host, so its bytes are not the runtime's own. The
|
|
# bundled rcheevos reader gets blobs in heap buffers of
|
|
# exactly their size: every truncation of a real one, a chunk
|
|
# length that wraps the offset back before itself, and a
|
|
# digest cut short. A read past a blob fails under ASan; the
|
|
# wrap would loop forever, which the timeout ends.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
ASAN_OPTIONS=detect_leaks=1 timeout 60 ./progress_bounds_test
|
|
make clean
|
|
make CC=x86_64-w64-mingw32-gcc TARGET=progress_bounds_test.exe
|
|
test -f progress_bounds_test.exe
|
|
make clean TARGET=progress_bounds_test.exe
|
|
echo "[pass] progress_bounds_test (ASan + UBSan, and a mingw-w64 build)"
|
|
|
|
- name: Build and run psp1_gfx_contracts_test (ASan + UBSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/psp1_gfx_contracts
|
|
run: |
|
|
set -eu
|
|
# gfx/drivers/psp1_gfx.c is reached only by the griffin unity
|
|
# build for PSP, so nothing else here compiles it, let alone
|
|
# runs it. This links the real driver against a stand-in for
|
|
# the parts of the machine its correctness turns on: main RAM
|
|
# and VRAM each mapped twice so the cached and uncached
|
|
# aliases genuinely alias, a no-access page after VRAM so a
|
|
# blit off the end faults, display lists that stay the GE's
|
|
# until they are waited on, and a vblank sub-interrupt that
|
|
# can be fired from inside free().
|
|
#
|
|
# What it holds: the 24bpp BGR byte order video_driver.h
|
|
# specifies for read_viewport, filling the whole buffer the
|
|
# screenshot task hands over, releasing the interrupt handler
|
|
# before the driver struct it writes through, keeping the GE
|
|
# off a display list or framebuffer that is in use, both blits
|
|
# staying inside the buffers they target, cache-line ownership
|
|
# of everything the GE writes, and init failing cleanly when
|
|
# an allocation does.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
rc=0
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
timeout 120 ./psp1_gfx_contracts_test || rc=$?
|
|
# 77: the runner would not hand over the PSP address windows.
|
|
if [ "$rc" = 77 ]; then
|
|
echo "[skip] PSP address windows unavailable"
|
|
exit 0
|
|
fi
|
|
[ "$rc" = 0 ]
|
|
make clean
|
|
|
|
- name: Shared media transport controls (C89, ASan, UBSan, TSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/media_controls
|
|
run: |
|
|
set -eu
|
|
make -B check THREADS=0
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=halt_on_error=1 make -B check SANITIZER=address,undefined
|
|
TSAN_OPTIONS=halt_on_error=1 make -B check SANITIZER=thread
|
|
|
|
- name: MinGW strict C89 float math
|
|
run: CC=x86_64-w64-mingw32-gcc WINEDEBUG=-all python3 samples/gfx/mingw_c89_math/run.py
|
|
|
|
- name: Vulkan synchronous readback slot
|
|
run: python3 samples/gfx/vulkan_readback_slot/run.py
|
|
|
|
- name: D3D11 resized readback destination guard
|
|
run: python3 samples/gfx/d3d11_readback_resize/run.py
|
|
|
|
- name: Build and run hw_ring_fence_test (ASan + UBSan, TSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/hw_ring_fence
|
|
run: |
|
|
set -u
|
|
set -o pipefail
|
|
# The hardware ring's fence handoff between the video thread
|
|
# and the core's thread (gfx/video_thread_hw_fence.h), the
|
|
# tree's own header, with two threads at one slot: a fence is
|
|
# signalled only while idle, waited only once signalled, and
|
|
# never inside two calls at once - with no lock. The single
|
|
# re-signalled fence it replaced is built too and must fail.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
timeout 120 make check SANITIZER=address,undefined
|
|
echo "[pass] hw_ring_fence_test (ASan + UBSan)"
|
|
# The two threads share nothing but atomics, and TSan is
|
|
# what says so.
|
|
make clean SANITIZER=thread && make -j$(nproc) all SANITIZER=thread
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 120 make check SANITIZER=thread
|
|
echo "[pass] hw_ring_fence_test (TSan)"
|
|
make clean
|
|
|
|
- name: Build and run hw_ring_queue_test (ASan + UBSan, TSan)
|
|
working-directory: samples/gfx/hw_ring_queue
|
|
shell: bash
|
|
run: |
|
|
set -euo pipefail
|
|
make clean
|
|
make check SANITIZER=address,undefined
|
|
make clean
|
|
make check SANITIZER=thread
|
|
make clean
|
|
|
|
- name: ccache statistics
|
|
if: ${{ !cancelled() }}
|
|
continue-on-error: true
|
|
run: ccache -sv
|
|
|
|
- name: Save ccache
|
|
# Only master's pushes keep a cache: pull requests restore
|
|
# master's, so one push does not evict the next one's.
|
|
if: success() && github.event_name == 'push' && github.ref == 'refs/heads/master'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.ccache
|
|
key: ccache-${{ github.workflow }}-${{ github.job }}-${{ github.sha }}
|
|
|
|
thumbnail-preview:
|
|
name: The file browser's video preview (gfx_thumbnail_preview)
|
|
runs-on: ubuntu-latest
|
|
# Two sanitizer builds, the video harness under each, and the
|
|
# windowed-animation harness under ASan over ~70 MB of lossless
|
|
# frames: 5 to 9 minutes, in parallel with samples-gfx rather than
|
|
# inside its budget.
|
|
timeout-minutes: 20
|
|
env:
|
|
APT_PKGS: ffmpeg ccache zlib1g-dev
|
|
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@v5
|
|
|
|
- name: Apt cache key
|
|
id: apt
|
|
run: sh .github/workflows/scripts/apt-cache-key.sh "$APT_PKGS"
|
|
|
|
- name: Restore apt cache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
restore-keys: ${{ steps.apt.outputs.prefix }}
|
|
|
|
- name: Install dependencies
|
|
timeout-minutes: 15
|
|
run: sh .github/workflows/scripts/apt-install.sh $APT_PKGS
|
|
|
|
- name: Save apt cache
|
|
if: env.APT_CACHE_SAVE == '1'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.apt-cache
|
|
key: ${{ steps.apt.outputs.key }}
|
|
|
|
- name: Link with lld
|
|
run: sh .github/workflows/scripts/use-lld.sh
|
|
|
|
- name: Restore ccache
|
|
uses: actions/cache/restore@v4
|
|
with:
|
|
path: ~/.ccache
|
|
key: ccache-${{ github.workflow }}-${{ github.job }}-${{ github.sha }}
|
|
restore-keys: |
|
|
ccache-${{ github.workflow }}-${{ github.job }}-
|
|
|
|
- name: Enable ccache
|
|
run: |
|
|
set -eu
|
|
export CCACHE_DIR="$HOME/.ccache"
|
|
echo "CCACHE_DIR=$CCACHE_DIR" >> "$GITHUB_ENV"
|
|
echo "/usr/lib/ccache" >> "$GITHUB_PATH"
|
|
ccache -M 250M
|
|
ccache -z
|
|
|
|
- name: Build and run gfx_thumbnail_preview_test (ASan + UBSan, TSan)
|
|
shell: bash
|
|
working-directory: samples/gfx/gfx_thumbnail_preview
|
|
run: |
|
|
set -eu
|
|
# Regression test for the file-browser video preview in
|
|
# gfx/gfx_thumbnail.c, covering the windowed open, the
|
|
# anim-first status/alpha handshake, and the preview-audio
|
|
# hand-off to libretro-common/audio/audio_mixer.c.
|
|
#
|
|
# Every fault this pins was invisible to a decode-level
|
|
# test, which is why they shipped one after another:
|
|
#
|
|
# - the windowed open produced a texture and reached
|
|
# AVAILABLE but nothing raised alpha, so every video
|
|
# decoded perfectly and drew at zero opacity;
|
|
# - a LEADING moov left the demuxer's avail below the
|
|
# media floor, so the first sample was unreachable, and
|
|
# since the feeder anchors at tell==0 nothing ever
|
|
# raised the bound - a standstill with no way out;
|
|
# - a TRAILING moov cannot be reached by a resident head
|
|
# at all, so preview audio was silent on every such
|
|
# file until the moov island was committed instead;
|
|
# - the mixer hand-off copied the whole container out of
|
|
# a partially-committed window (SIGSEGV), took an
|
|
# int32_t length that truncated a 7 GB container to
|
|
# 3.5 GB, dropped every feeder raise for want of an
|
|
# M4A arm in audio_mixer_voice_set_avail (the same
|
|
# second of audio on a loop), and applied the resident
|
|
# bound only under HAVE_ROPUS.
|
|
#
|
|
# So this links the REAL gfx_thumbnail.c AND the REAL
|
|
# audio_mixer.c. The earlier harness stubbed the mixer and
|
|
# was therefore modelling the two functions three of those
|
|
# defects lived in. The stubbed audio thread consumes at a
|
|
# settable rate and TOUCHES every byte it is told is
|
|
# resident, so an over-claimed avail faults under ASan
|
|
# rather than passing quietly, and it counts feeder stalls
|
|
# (excluding the file end, where hitting the wall is the
|
|
# loop point rather than a starved feeder).
|
|
#
|
|
# The fixtures are container LAYOUTS, not clips - where the
|
|
# moov sits, whether media starts past 4 GiB, whether the
|
|
# moov outgrows the head - because layout is what every one
|
|
# of these faults turned on. The multi-gigabyte ones are
|
|
# sparse: two 7.28 GB files cost ~400 KB of disk.
|
|
#
|
|
# The windowed-animation harness below decodes its two
|
|
# lossless fixtures three times each; they are sized just past
|
|
# what the feeder may keep resident (~43 MB and ~39 MB), and
|
|
# mkfixtures.py reuses an existing set, so the second
|
|
# "make fixtures" (TSan pass) costs nothing. ~55 s under
|
|
# ASan on one core.
|
|
# Frame counts are set from measurement, not taste. At 300
|
|
# ASan / 200 TSan frames this step took ~5.6 min on one
|
|
# core, against a job budget shared with a dozen others;
|
|
# 120 / 60 brings it to ~1.5 min and still crosses every
|
|
# window boundary the assertions rest on (the feeder moves
|
|
# at AUDIO_BYTES_PER_TICK, not per frame). The job timeout
|
|
# goes to 20 min to leave headroom for the slowest runner.
|
|
make clean SANITIZER=address,undefined && make -j$(nproc) all SANITIZER=address,undefined
|
|
make fixtures
|
|
test -x gfx_thumbnail_preview_test
|
|
ASAN_OPTIONS=detect_leaks=1:allocator_may_return_null=1 \
|
|
UBSAN_OPTIONS=print_stacktrace=1 \
|
|
AUDIO_BYTES_PER_TICK=65536 FRAMES=120 timeout 300 \
|
|
./gfx_thumbnail_preview_test fixtures/*.mp4 fixtures/anim_lossless.webp
|
|
echo "[pass] gfx_thumbnail_preview_test (ASan)"
|
|
mkdir -p fixtures/packet_span
|
|
ffmpeg -v error -y -f lavfi \
|
|
-i 'testsrc2=size=1280x720:rate=1,noise=alls=100:allf=t+u:all_seed=10' \
|
|
-t 3 -c:v libx264 -preset ultrafast -qp 0 -pix_fmt yuv420p \
|
|
-moov_size 16777216 fixtures/packet_span/large.mp4
|
|
for bin in gfx_thumbnail_preview_test gfx_thumbnail_preview_test_threaded; do
|
|
ASAN_OPTIONS=detect_leaks=1 MP4_PACKET_SPAN=1 timeout 120 \
|
|
./$bin fixtures/packet_span/large.mp4
|
|
done
|
|
# HDR previews: with HDR_OUTPUT the stub driver keeps half
|
|
# floats and shows them as linear scRGB, and the PQ-tagged
|
|
# fixture must animate as them on the PQ EOTF of its grey; its
|
|
# SDR twin, and the HDR one without HDR output, must not upload
|
|
# a single half-float frame. Both decode paths: the poll's own
|
|
# here, the worker's jobs in the threaded build below.
|
|
HDRFX=../../formats/video_hdr_fp16/fixtures
|
|
for bin in gfx_thumbnail_preview_test gfx_thumbnail_preview_test_threaded; do
|
|
ASAN_OPTIONS=detect_leaks=1 NOAUDIO=1 THREADS=4 FRAMES=60 \
|
|
HDR_OUTPUT=1 HDR_EXPECT=1 timeout 120 ./$bin $HDRFX/hdr_hevc.mp4
|
|
ASAN_OPTIONS=detect_leaks=1 NOAUDIO=1 THREADS=4 FRAMES=60 \
|
|
HDR_OUTPUT=1 timeout 120 ./$bin $HDRFX/sdr_hevc.mp4
|
|
ASAN_OPTIONS=detect_leaks=1 NOAUDIO=1 THREADS=4 FRAMES=60 \
|
|
timeout 120 ./$bin $HDRFX/hdr_hevc.mp4
|
|
done
|
|
echo "[pass] gfx_thumbnail_preview HDR lanes"
|
|
# The same session with HAVE_THREADS: the thumbnail decodes on
|
|
# its worker and the pool, the app's own path, paced at a
|
|
# display's rate with a bound on the longest stall.
|
|
test -x gfx_thumbnail_preview_test_threaded
|
|
ASAN_OPTIONS=detect_leaks=1:allocator_may_return_null=1 \
|
|
UBSAN_OPTIONS=print_stacktrace=1 \
|
|
THREADS=4 FRAMES=180 PACE_HZ=60 PACE_MAX_STALL_MS=800 NOAUDIO=1 timeout 300 \
|
|
./gfx_thumbnail_preview_test_threaded fixtures/bframes_h264.mp4 fixtures/bframes_hevc.mp4
|
|
echo "[pass] gfx_thumbnail_preview_test_threaded (ASan, 4 threads)"
|
|
# The frame-indexed types (animated WEBP, APNG) through the
|
|
# same session, windowed: animated WEBP had no progressive
|
|
# open, so the session read the whole file into the
|
|
# reservation (for every still WEBP thumbnail too) and
|
|
# charged one window for it; APNG had the open but no byte
|
|
# cursor, so the feeder never moved past the 12 MB primed at
|
|
# open and a longer animation looped early forever. Mapping
|
|
# residency is measured with mincore, so the bound holds
|
|
# under the sanitizers. ASan+UBSan only: the harness is
|
|
# single-threaded (no TSan target), and the lossless decode
|
|
# of ~900 720p frames takes it past the job budget under
|
|
# TSan (>5 min) for no coverage.
|
|
ASAN_OPTIONS=detect_leaks=1 UBSAN_OPTIONS=print_stacktrace=1 \
|
|
timeout 60 ./preview_av1_hw_test
|
|
test -x preview_anim_window_test
|
|
ASAN_OPTIONS=detect_leaks=1:allocator_may_return_null=1 \
|
|
# V1 in this run plays long_video.mp4 (60 s at 4 Mbit/s,
|
|
# 29 MB, generated by mkfixtures) end to end through the
|
|
# session and requires the resident bytes to stay inside
|
|
# the bitrate-sized window: measured 5.25 MiB peak against
|
|
# the 20 MiB the fixed 4+8+8 MiB constants held before.
|
|
UBSAN_OPTIONS=print_stacktrace=1 timeout 600 \
|
|
./preview_anim_window_test fixtures
|
|
echo "[pass] preview_anim_window_test (ASan)"
|
|
# The decode test is the missing half of the harness above:
|
|
# the REAL mixer -> audio_transfer -> rmp4 -> raac chain over
|
|
# a budget-fed window, at budgets small enough that the
|
|
# decoder reaches the resident frontier. The stubbed audio
|
|
# thread above models exactly the layer these faults lived
|
|
# in: no wall over the packets on the trailing-moov path
|
|
# (SIGSEGV inside raac_decode_frame the moment the decoder
|
|
# outran the feeder), demuxer bounds that refused to lower,
|
|
# a wall stall taken for end of stream (mid-file rewinds,
|
|
# then the voice released), and set_avail compiling to a
|
|
# no-op without HAVE_RWEBM - the _norwebm build exists for
|
|
# that last one.
|
|
test -x preview_audio_decode_test
|
|
test -x preview_audio_decode_test_norwebm
|
|
ASAN_OPTIONS=detect_leaks=1:allocator_may_return_null=1 \
|
|
UBSAN_OPTIONS=print_stacktrace=1 timeout 600 bash -c '
|
|
./preview_audio_decode_test fixtures/trailing_large.mp4 1024 island
|
|
./preview_audio_decode_test fixtures/trailing_large.mp4 262144 island
|
|
./preview_audio_decode_test_norwebm fixtures/trailing_large.mp4 1024 island
|
|
./preview_audio_decode_test_norwebm fixtures/trailing_large.mp4 262144 island'
|
|
echo "[pass] preview_audio_decode_test (ASan)"
|
|
# The threaded decode against the single-threaded one, frame
|
|
# for frame: the colour conversion in row bands on a pool,
|
|
# for the VP9 fixture (four tile columns) the columns decoded
|
|
# side by side, for the HEVC fixture (WPP) the CTB rows on a
|
|
# wavefront. Every frame must be byte-identical.
|
|
test -x preview_blit_bands_test
|
|
ASAN_OPTIONS=detect_leaks=1:allocator_may_return_null=1 \
|
|
UBSAN_OPTIONS=print_stacktrace=1 timeout 300 bash -c '
|
|
./preview_blit_bands_test fixtures/long_video.mp4
|
|
./preview_blit_bands_test fixtures/vp9_tiles.webm
|
|
./preview_blit_bands_test fixtures/hevc_wpp.mp4
|
|
./preview_blit_bands_test fixtures/bframes_hevc.mp4
|
|
./preview_blit_bands_test fixtures/hevc_nowpp.mp4 fixtures/mbaff_h264.mp4
|
|
RH265_PUBLISH_DELAY=200 ./preview_blit_bands_test fixtures/hevc_wpp.mp4 fixtures/bframes_hevc.mp4 fixtures/hevc_nowpp.mp4'
|
|
echo "[pass] preview_blit_bands_test (ASan)"
|
|
# Catching up: behind the clock the stream drops only what
|
|
# nothing references, and every picture still shown is the
|
|
# full decode's; the threaded build does the same with four
|
|
# pictures in flight, the drops taken at submit.
|
|
test -x preview_catchup_test && test -x preview_catchup_test_threaded
|
|
ASAN_OPTIONS=detect_leaks=1:allocator_may_return_null=1 \
|
|
UBSAN_OPTIONS=print_stacktrace=1 timeout 300 bash -c '
|
|
./preview_catchup_test fixtures/bframes_h264.mp4 fixtures/bframes_hevc.mp4
|
|
./preview_catchup_test_threaded fixtures/bframes_h264.mp4 fixtures/bframes_hevc.mp4'
|
|
echo "[pass] preview_catchup_test (ASan, one picture at a time and four in flight)"
|
|
# The audio chain against ffmpeg: the track's rate exact, the
|
|
# resampled one within a fraction of a percent, from the first
|
|
# half second.
|
|
test -x preview_audio_oracle_test
|
|
ASAN_OPTIONS=detect_leaks=1:allocator_may_return_null=1 \
|
|
UBSAN_OPTIONS=print_stacktrace=1 timeout 300 \
|
|
./preview_audio_oracle_test fixtures/trailing_small.mp4 fixtures/trailing_large.mp4
|
|
echo "[pass] preview_audio_oracle_test (ASan)"
|
|
./preview_audio_feed_test fixtures/audio_large_packet.m4a
|
|
./preview_audio_feed_test --prepared-only fixtures/audio_seek_gap.m4a
|
|
make audio-timeline-check SANITIZER=address,undefined
|
|
./preview_audio_feed_test --prepared-webm fixtures/audio-movie-timeline/timeline-aac-0.mkv fixtures/audio-movie-timeline/timeline-aac-1.mkv
|
|
./preview_audio_feed_test --prepared-webm fixtures/audio_seek_opus.weba fixtures/audio_seek_vorbis.weba fixtures/audio_seek_opus_window.webm fixtures/audio_seek_vorbis_window.webm
|
|
CPU_GAP=1 ./gfx_thumbnail_preview_test fixtures/video_gap_head.webm
|
|
CPU_GAP=1 ./gfx_thumbnail_preview_test_threaded fixtures/video_gap_head.webm
|
|
CPU_GAP=1 CPU_GAP_FRAMES=48 ./gfx_thumbnail_preview_test fixtures/video_gap_pin.webm
|
|
CPU_GAP=1 CPU_GAP_FRAMES=48 ./gfx_thumbnail_preview_test_threaded fixtures/video_gap_pin.webm
|
|
CPU_GAP=1 CPU_GROUP=1 CPU_GAP_FRAMES=48 ./gfx_thumbnail_preview_test fixtures/video_group.webm
|
|
CPU_GAP=1 CPU_GROUP=1 CPU_GAP_FRAMES=48 ./gfx_thumbnail_preview_test_threaded fixtures/video_group.webm
|
|
GROUP_SEEK=1 ./gfx_thumbnail_preview_test fixtures/video_group.webm
|
|
GROUP_SEEK=1 ./gfx_thumbnail_preview_test_threaded fixtures/video_group.webm
|
|
for codec in opus vorbis; do
|
|
python3 webm_audio_padding.py --audio-group-void fixtures/audio_group_"$codec".webm fixtures/audio_refill_"$codec".webm
|
|
done
|
|
python3 webm_audio_padding.py --audio-group-void fixtures/audio_group_aac.mkv fixtures/audio_refill_aac.mkv
|
|
./preview_audio_feed_test --continuous-webm fixtures/audio_refill_opus.webm
|
|
./preview_audio_feed_test --continuous-webm fixtures/audio_refill_vorbis.webm
|
|
AUDIO_MOVIE_LOOPS=1 ./preview_audio_feed_test --continuous-webm fixtures/audio_refill_aac.mkv
|
|
./preview_audio_feed_test --prepared-webm fixtures/audio_group_aac.mkv
|
|
./preview_audio_feed_test --prepared-webm fixtures/audio_group_opus.webm fixtures/audio_group_vorbis.webm
|
|
AUDIO_FEED_RACE=1 ./preview_audio_feed_test_threaded --continuous-webm fixtures/audio_refill_opus.webm
|
|
AUDIO_FEED_RACE=1 ./preview_audio_feed_test_threaded --continuous-webm fixtures/audio_refill_vorbis.webm
|
|
make -B -j2 preview_audio_feed_test preview_audio_feed_test_threaded COUNT_IO=1 SANITIZER=address,undefined
|
|
./preview_audio_feed_test --seek-batch
|
|
./preview_audio_feed_test_threaded --seek-batch
|
|
CPU_MEDIA=1 TRANSPORT_PAUSE=1 TRANSPORT_SEEK=1 ./gfx_thumbnail_preview_test fixtures/trailing_small.mp4
|
|
CPU_MEDIA=1 TRANSPORT_PAUSE=1 TRANSPORT_SEEK=1 ./gfx_thumbnail_preview_test_threaded fixtures/trailing_small.mp4
|
|
python3 movie_timeline_fixtures.py fixtures/movie-timeline
|
|
for fixture in video-timeline-1.mp4 video-timeline-libopus.webm video-timeline-libvorbis.webm; do
|
|
FRONTEND_TIMELINE=1 ./gfx_thumbnail_preview_test fixtures/movie-timeline/"$fixture"
|
|
FRONTEND_TIMELINE=1 ./gfx_thumbnail_preview_test_threaded fixtures/movie-timeline/"$fixture"
|
|
done
|
|
# clean keeps fixtures/ by design; regenerate anyway so the
|
|
# step cannot silently run against an empty glob.
|
|
make clean SANITIZER=thread && make -j$(nproc) all SANITIZER=thread
|
|
make fixtures
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 60 ./preview_av1_hw_test
|
|
TSAN_OPTIONS=halt_on_error=1:allocator_may_return_null=1 FRAMES=60 timeout 300 \
|
|
./gfx_thumbnail_preview_test fixtures/*.mp4 fixtures/anim_lossless.webp
|
|
echo "[pass] gfx_thumbnail_preview_test (TSan)"
|
|
# and threaded under TSan, which is the run that can see a
|
|
# race between the worker, the pool and the poll
|
|
THREADS=4 FRAMES=180 PACE_HZ=60 PACE_MAX_STALL_MS=1500 NOAUDIO=1 timeout 300 \
|
|
./gfx_thumbnail_preview_test_threaded fixtures/bframes_h264.mp4 fixtures/bframes_hevc.mp4
|
|
echo "[pass] gfx_thumbnail_preview_test_threaded (TSan, 4 threads)"
|
|
CPU_MEDIA=1 TRANSPORT_PAUSE=1 TRANSPORT_SEEK=1 TSAN_OPTIONS=halt_on_error=1 timeout 120 ./gfx_thumbnail_preview_test_threaded fixtures/trailing_small.mp4
|
|
for fixture in video-timeline-1.mp4 video-timeline-libopus.webm video-timeline-libvorbis.webm; do
|
|
FRONTEND_TIMELINE=1 TSAN_OPTIONS=halt_on_error=1 timeout 120 ./gfx_thumbnail_preview_test_threaded fixtures/movie-timeline/"$fixture"
|
|
done
|
|
for codec in opus vorbis; do
|
|
AUDIO_MOVIE_LOOPS=1 AUDIO_FEED_RACE=1 TSAN_OPTIONS=halt_on_error=1 timeout 120 \
|
|
./preview_audio_feed_test_threaded --continuous-webm fixtures/audio_refill_"$codec".webm
|
|
done
|
|
AUDIO_MOVIE_LOOPS=1 AUDIO_FEED_RACE=1 TSAN_OPTIONS=halt_on_error=1 timeout 120 \
|
|
./preview_audio_feed_test_threaded --continuous-webm fixtures/audio_refill_aac.mkv
|
|
# The pool and the tile-column threads under TSan: the
|
|
# shared frame buffers and contexts are written in disjoint
|
|
# columns, the counts merged after the join.
|
|
TSAN_OPTIONS=halt_on_error=1:allocator_may_return_null=1 timeout 300 bash -c '
|
|
./preview_blit_bands_test fixtures/long_video.mp4
|
|
./preview_blit_bands_test fixtures/vp9_tiles.webm
|
|
./preview_blit_bands_test fixtures/hevc_wpp.mp4
|
|
./preview_blit_bands_test fixtures/bframes_hevc.mp4
|
|
./preview_blit_bands_test fixtures/hevc_nowpp.mp4 fixtures/mbaff_h264.mp4
|
|
RH265_PUBLISH_DELAY=200 ./preview_blit_bands_test fixtures/hevc_wpp.mp4 fixtures/bframes_hevc.mp4 fixtures/hevc_nowpp.mp4'
|
|
echo "[pass] preview_blit_bands_test (TSan)"
|
|
TSAN_OPTIONS=halt_on_error=1:allocator_may_return_null=1 timeout 300 bash -c '
|
|
./preview_catchup_test_threaded fixtures/bframes_h264.mp4 fixtures/bframes_hevc.mp4'
|
|
echo "[pass] preview_catchup_test_threaded (TSan)"
|
|
TSAN_OPTIONS=halt_on_error=1 timeout 600 bash -c '
|
|
./preview_audio_decode_test fixtures/trailing_large.mp4 1024 island
|
|
./preview_audio_decode_test fixtures/trailing_large.mp4 262144 island'
|
|
echo "[pass] preview_audio_decode_test (TSan)"
|
|
|
|
- name: Save ccache
|
|
# Only master's pushes keep a cache, as in samples-gfx
|
|
if: success() && github.event_name == 'push' && github.ref == 'refs/heads/master'
|
|
uses: actions/cache/save@v4
|
|
with:
|
|
path: ~/.ccache
|
|
key: ccache-${{ github.workflow }}-${{ github.job }}-${{ github.sha }}
|