El nivel más bajo que tiene sentido desde mi punto de vista es algo que habla de los recursos involucrados en el renderizado: vb / ib, superficies de renderizado, texturas, sombreadores, bloques de estado, etc.
El problema aquí es que algunos de estos deben estar en diferentes formatos, dependiendo de la API; ahí es donde se vuelve un poco complicado. La forma más fácil de evitarlo es preprocesar los recursos estáticos para la API respectiva. Para los dinámicos, use solo sombreadores para generarlos, lo que hace que sea bastante sencillo permanecer en formatos nativos.
Todo lo que haces en el nivel superior es configurar tuberías con recursos adjuntos y entregarlas a la GPU. Descubrirá que no todo se puede resumir de esa manera, especialmente si aprovecha los trucos específicos del hardware. Pero es un buen comienzo.
(Nota al margen: si trata los trucos específicos de la plataforma como un tipo especial de recurso, puede llevar todo este concepto bastante lejos).
Entonces, de alguna manera, creará dos cosas: un administrador de recursos de hardware, más un kit de herramientas para configurar un DAG de estos recursos.